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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50df5df0e40d9cd127c03bacec3905c401be05ab
|
4
|
+
data.tar.gz: 071d750fe875bcdaa4308fde9e6bf280b18c5d3e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6fbfb97c8e5f30a23d1260c73046e5f1e19941b0cdaad796ae490c8ebcbf73eeacc3eb3cfc1cd974f80e58b7c31e56d7366e3311e6af9146da055a64c410f25
|
7
|
+
data.tar.gz: 57c6cdba6764b876efbe81d85f98285c5945b85f0571d8fa715806f2210b94b317698b06a957171ef78ea15fc23d2c21e904eb6f2f250e9036270f5217d26fd7
|
data/.rubocop.yml
CHANGED
data/.travis.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
cache: bundler
|
2
|
+
|
3
|
+
language: ruby
|
4
|
+
|
5
|
+
services:
|
6
|
+
- mysql
|
7
|
+
- postgresql
|
8
|
+
|
9
|
+
rvm:
|
10
|
+
- '2.2.7'
|
11
|
+
- '2.3.4'
|
12
|
+
- '2.4.1'
|
13
|
+
|
14
|
+
before_install: gem update bundler
|
15
|
+
before_script:
|
16
|
+
- psql -c 'CREATE DATABASE reports_kit_test;' -U postgres
|
17
|
+
- mysql -e 'CREATE DATABASE IF NOT EXISTS reports_kit_test;'
|
18
|
+
- bundle exec appraisal install
|
19
|
+
|
20
|
+
script: bundle exec appraisal rspec
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
ReportsKit
|
2
2
|
=====
|
3
|
+
[![Build Status](https://travis-ci.org/tombenner/reports_kit.svg?branch=master)](https://travis-ci.org/tombenner/reports_kit)
|
4
|
+
|
3
5
|
ReportsKit lets you easily create beautiful charts with customizable, interactive filters.
|
4
6
|
|
5
7
|
For interactive examples, see [reportskit.co](https://www.reportskit.co/).
|
@@ -63,35 +65,27 @@ end
|
|
63
65
|
Quick Start
|
64
66
|
-----------
|
65
67
|
|
66
|
-
After installation, you can create your first chart with a single line
|
67
|
-
|
68
|
-
In any view, create a chart that shows the number of records of a model (e.g. `user`) created over time:
|
69
|
-
|
70
|
-
`app/views/users/index.html.haml`
|
71
|
-
```haml
|
72
|
-
= render_report measure: { key: 'user', dimensions: ['created_at'] }
|
73
|
-
```
|
74
|
-
|
75
|
-
You're done! `render_report` will render the following chart:
|
76
|
-
|
77
|
-
[<img src="docs/images/users_by_created_at.png?raw=true" width="500" />](docs/images/users_by_created_at.png?raw=true)
|
68
|
+
After installation, you can create your first chart with just a YAML file and a single line in any view.
|
78
69
|
|
79
|
-
|
70
|
+
Configure the chart in the YAML file:
|
80
71
|
|
81
72
|
`config/reports_kit/reports/my_users.yml`
|
82
73
|
```yaml
|
83
|
-
measure:
|
84
|
-
|
85
|
-
|
86
|
-
- created_at
|
74
|
+
measure: user
|
75
|
+
dimensions:
|
76
|
+
- created_at
|
87
77
|
```
|
88
78
|
|
79
|
+
Then pass that filename to `render_report` in a view:
|
80
|
+
|
89
81
|
`app/views/users/index.html.haml`
|
90
82
|
```haml
|
91
83
|
= render_report 'my_users'
|
92
84
|
```
|
93
85
|
|
94
|
-
|
86
|
+
You're done! `render_report` will render the following chart:
|
87
|
+
|
88
|
+
[<img src="docs/images/users_by_created_at.png?raw=true" width="500" />](docs/images/users_by_created_at.png?raw=true)
|
95
89
|
|
96
90
|
### Form Controls
|
97
91
|
|
@@ -109,7 +103,11 @@ Many other form controls are available; see [Filters](docs/filters.md) for more.
|
|
109
103
|
|
110
104
|
### How It Works
|
111
105
|
|
112
|
-
In the Quick Start chart, `
|
106
|
+
In the Quick Start chart, `measure: user` tells ReportsKit to count the number of `User` records, and `dimensions: ['created_at']` tells it to group by the week of the `created_at` column. Since `created_at` is a `datetime` column, ReportsKit knows that it should group the counts by week (the granularity is configurable), sort them chronologically, and add in zeros for any missing weeks.
|
107
|
+
|
108
|
+
ReportsKit infers sane defaults from your ActiveRecord model configurations. If there was a `belongs_to :company` association on `User` and you used `dimensions: ['company']`, then ReportsKit would count users grouped by the `company_id` column and show company names on the x-axis.
|
109
|
+
|
110
|
+
If you need more customization (e.g. custom filters, custom dimensions, custom aggregation functions, custom orders, aggregations of aggregations, etc), ReportsKit is very flexible and powerful and supports all of these with a simple syntax. It lets you use SQL, too.
|
113
111
|
|
114
112
|
To learn how to use more of ReportsKit's features, check out the following resources:
|
115
113
|
|
@@ -3,6 +3,7 @@ window.ReportsKit = {};
|
|
3
3
|
$(document).ready(function() {
|
4
4
|
$('.reports_kit_report').each(function(index, el) {
|
5
5
|
var el = $(el)
|
6
|
-
|
6
|
+
var reportClass = el.data('report-class');
|
7
|
+
new ReportsKit[reportClass]().render({ 'el': el });
|
7
8
|
});
|
8
9
|
});
|
@@ -6,7 +6,8 @@ ReportsKit.Chart = (function(options) {
|
|
6
6
|
self.report = options.report;
|
7
7
|
self.el = self.report.el;
|
8
8
|
|
9
|
-
self.
|
9
|
+
self.defaultEmptyStateText = 'No data was found';
|
10
|
+
self.emptyStateEl = $('<div>' + self.defaultEmptyStateText + '</div>').appendTo(self.report.visualizationEl).hide();
|
10
11
|
self.loadingIndicatorEl = $('<div class="loading_indicator"></div>').appendTo(self.report.visualizationEl).hide();
|
11
12
|
self.canvas = $('<canvas />').appendTo(self.report.visualizationEl);
|
12
13
|
};
|
@@ -15,19 +16,32 @@ ReportsKit.Chart = (function(options) {
|
|
15
16
|
var path = self.el.data('path');
|
16
17
|
var separator = path.indexOf('?') === -1 ? '?' : '&';
|
17
18
|
path += separator + 'properties=' + encodeURIComponent(JSON.stringify(self.report.properties()));
|
18
|
-
self.loadingIndicatorEl.fadeIn(
|
19
|
+
self.loadingIndicatorEl.fadeIn(1000);
|
20
|
+
if (self.canvas.is(':visible')) {
|
21
|
+
self.canvas.fadeTo(300, 0.1);
|
22
|
+
}
|
19
23
|
$.getJSON(path, function(response) {
|
20
24
|
var data = response.data;
|
21
25
|
var chartData = data.chart_data;
|
26
|
+
var isEmptyState = chartData.datasets.length === 0 ||
|
27
|
+
(chartData.datasets.length === 1 && chartData.datasets[0].data.length === 0);
|
28
|
+
var emptyStateText = (data.report_options && data.report_options.empty_state_text) || self.defaultEmptyStateText;
|
22
29
|
var options = chartData.options;
|
23
|
-
options = self.addAdditionalOptions(options,
|
30
|
+
options = self.addAdditionalOptions(options, data.report_options);
|
31
|
+
|
32
|
+
self.loadingIndicatorEl.stop(true, true).hide();
|
33
|
+
self.emptyStateEl.html(emptyStateText).toggle(isEmptyState);
|
34
|
+
if (isEmptyState) {
|
35
|
+
self.canvas.hide();
|
36
|
+
return;
|
37
|
+
}
|
38
|
+
self.canvas.show().fadeTo(300, 1);
|
24
39
|
|
25
40
|
var args = {
|
26
41
|
type: data.type,
|
27
42
|
data: chartData,
|
28
43
|
options: options
|
29
44
|
};
|
30
|
-
self.loadingIndicatorEl.stop(true, true).hide();
|
31
45
|
|
32
46
|
if (self.chart) {
|
33
47
|
self.chart.data.datasets = chartData.datasets;
|
@@ -36,24 +50,19 @@ ReportsKit.Chart = (function(options) {
|
|
36
50
|
} else {
|
37
51
|
self.chart = new Chart(self.canvas, args);
|
38
52
|
}
|
39
|
-
self.noResultsEl.toggle(self.chart.data.labels.length === 0);
|
40
53
|
});
|
41
54
|
};
|
42
55
|
|
43
|
-
self.addAdditionalOptions = function(options,
|
56
|
+
self.addAdditionalOptions = function(options, reportOptions) {
|
44
57
|
var additionalOptions = {};
|
45
|
-
var maxItems =
|
58
|
+
var maxItems = reportOptions && reportOptions.legend && reportOptions.legend.max_items;
|
46
59
|
if (maxItems) {
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
}
|
53
|
-
}
|
54
|
-
}
|
60
|
+
options.legend = options.legend || {};
|
61
|
+
options.legend.labels = options.legend.labels || {};
|
62
|
+
options.legend.labels.filter = options.legend.labels.filter || function(item) {
|
63
|
+
var index = (typeof item.datasetIndex === 'undefined') ? item.index : item.datasetIndex;
|
64
|
+
return index < maxItems;
|
55
65
|
};
|
56
|
-
options = $.extend(true, options, additionalOptions);
|
57
66
|
}
|
58
67
|
return options;
|
59
68
|
};
|
@@ -1,8 +1,7 @@
|
|
1
|
-
ReportsKit.Report = (function(
|
1
|
+
ReportsKit.Report = (function() {
|
2
2
|
var self = this;
|
3
3
|
|
4
|
-
self.
|
5
|
-
self.options = options;
|
4
|
+
self.render = function(options) {
|
6
5
|
self.el = options.el;
|
7
6
|
self.visualizationEl = self.el.find('.reports_kit_visualization');
|
8
7
|
|
@@ -11,33 +10,25 @@ ReportsKit.Report = (function(options) {
|
|
11
10
|
|
12
11
|
self.initializeElements();
|
13
12
|
self.initializeEvents();
|
13
|
+
self.initializeVisualization();
|
14
|
+
self.renderVisualization();
|
15
|
+
};
|
14
16
|
|
17
|
+
self.initializeVisualization = function() {
|
15
18
|
if (self.defaultProperties.format == 'table') {
|
16
19
|
self.visualization = new ReportsKit.Table({ report: self });
|
17
20
|
} else {
|
18
21
|
self.visualization = new ReportsKit.Chart({ report: self });
|
19
22
|
}
|
20
|
-
self.render();
|
21
23
|
};
|
22
24
|
|
23
25
|
self.initializeElements = function() {
|
24
26
|
self.exportButtons = self.el.find('[data-role=reports_kit_export_button]');
|
25
|
-
self.
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
'Today': [moment(), moment()],
|
31
|
-
'Last 7 Days': [moment().subtract(7, 'days'), moment()],
|
32
|
-
'Last 30 Days': [moment().subtract(30, 'days'), moment()],
|
33
|
-
'Last 2 Months': [moment().subtract(2, 'months'), moment()],
|
34
|
-
'Last 3 Months': [moment().subtract(3, 'months'), moment()],
|
35
|
-
'Last 4 Months': [moment().subtract(4, 'months'), moment()],
|
36
|
-
'Last 6 Months': [moment().subtract(6, 'months'), moment()],
|
37
|
-
'Last 12 Months': [moment().subtract(12, 'months'), moment()],
|
38
|
-
'Year To Date': [moment().startOf('year'), moment()]
|
39
|
-
}
|
40
|
-
});
|
27
|
+
self.initializeAutocompletes();
|
28
|
+
self.initializeDateRangePickers();
|
29
|
+
};
|
30
|
+
|
31
|
+
self.initializeAutocompletes = function() {
|
41
32
|
self.form.find('.select2').each(function(index, el) {
|
42
33
|
el = $(el);
|
43
34
|
var path = el.data('path');
|
@@ -67,12 +58,40 @@ ReportsKit.Report = (function(options) {
|
|
67
58
|
});
|
68
59
|
};
|
69
60
|
|
61
|
+
self.initializeDateRangePickers = function() {
|
62
|
+
self.form.find('.date_range_picker').daterangepicker({
|
63
|
+
opens: 'left',
|
64
|
+
drops: 'down',
|
65
|
+
showDropdowns: false,
|
66
|
+
showWeekNumbers: false,
|
67
|
+
timePicker: false,
|
68
|
+
buttonClasses: ['btn', 'btn-sm'],
|
69
|
+
applyClass: 'btn-primary btn-daterange-submit',
|
70
|
+
cancelClass: 'btn-default',
|
71
|
+
maxDate: moment(),
|
72
|
+
locale: {
|
73
|
+
format: 'MMM D, YYYY'
|
74
|
+
},
|
75
|
+
ranges: {
|
76
|
+
'Today': [moment(), moment()],
|
77
|
+
'Last 7 Days': [moment().subtract(7, 'days'), moment()],
|
78
|
+
'Last 30 Days': [moment().subtract(30, 'days'), moment()],
|
79
|
+
'Last 2 Months': [moment().subtract(2, 'months'), moment()],
|
80
|
+
'Last 3 Months': [moment().subtract(3, 'months'), moment()],
|
81
|
+
'Last 4 Months': [moment().subtract(4, 'months'), moment()],
|
82
|
+
'Last 6 Months': [moment().subtract(6, 'months'), moment()],
|
83
|
+
'Last 12 Months': [moment().subtract(12, 'months'), moment()],
|
84
|
+
'Year To Date': [moment().startOf('year'), moment()]
|
85
|
+
}
|
86
|
+
});
|
87
|
+
};
|
88
|
+
|
70
89
|
self.initializeEvents = function() {
|
71
90
|
self.form.find('select,input').on('change', function() {
|
72
|
-
self.
|
91
|
+
self.renderVisualization();
|
73
92
|
})
|
74
93
|
self.form.on('submit', function() {
|
75
|
-
self.
|
94
|
+
self.renderVisualization();
|
76
95
|
return false;
|
77
96
|
})
|
78
97
|
self.exportButtons.on('click', self.onClickExportButton);
|
@@ -110,11 +129,9 @@ ReportsKit.Report = (function(options) {
|
|
110
129
|
return false;
|
111
130
|
};
|
112
131
|
|
113
|
-
self.
|
132
|
+
self.renderVisualization = function() {
|
114
133
|
self.visualization.render();
|
115
134
|
};
|
116
135
|
|
117
|
-
self.initialize(options);
|
118
|
-
|
119
136
|
return self;
|
120
137
|
});
|
@@ -6,6 +6,8 @@ ReportsKit.Table = (function(options) {
|
|
6
6
|
self.report = options.report;
|
7
7
|
self.el = self.report.el;
|
8
8
|
|
9
|
+
self.defaultEmptyStateText = 'No data was found';
|
10
|
+
self.emptyStateEl = $('<div>' + self.defaultEmptyStateText + '</div>').appendTo(self.report.visualizationEl).hide();
|
9
11
|
self.loadingIndicatorEl = $('<div class="loading_indicator"></div>').appendTo(self.report.visualizationEl).hide();
|
10
12
|
self.table = $('<table />', { 'class': 'table table-striped table-hover' }).appendTo(self.report.visualizationEl);
|
11
13
|
};
|
@@ -14,19 +16,37 @@ ReportsKit.Table = (function(options) {
|
|
14
16
|
var path = self.el.data('path');
|
15
17
|
var separator = path.indexOf('?') === -1 ? '?' : '&';
|
16
18
|
path += separator + 'properties=' + encodeURIComponent(JSON.stringify(self.report.properties()));
|
17
|
-
self.loadingIndicatorEl.fadeIn(
|
19
|
+
self.loadingIndicatorEl.fadeIn(1000);
|
20
|
+
if (self.table.is(':visible')) {
|
21
|
+
self.table.fadeTo(300, 0.1);
|
22
|
+
}
|
18
23
|
$.getJSON(path, function(response) {
|
19
24
|
var data = response.data;
|
20
25
|
var tableData = data.table_data;
|
26
|
+
var reportOptions = data.report_options || {};
|
27
|
+
// If the data only includes column headers, then it we have an empty state.
|
28
|
+
var isEmptyState = tableData.length <= 1;
|
29
|
+
var emptyStateText = reportOptions.empty_state_text || self.defaultEmptyStateText;
|
30
|
+
self.emptyStateEl.html(emptyStateText);
|
21
31
|
|
22
32
|
self.loadingIndicatorEl.stop(true, true).hide();
|
33
|
+
self.emptyStateEl.toggle(isEmptyState);
|
34
|
+
if (isEmptyState) {
|
35
|
+
self.table.hide();
|
36
|
+
return;
|
37
|
+
}
|
38
|
+
self.table.show().fadeTo(300, 1);
|
23
39
|
|
40
|
+
var rowAggregationsCount = self.rowAggregationsCount(reportOptions);
|
41
|
+
var rowAggregationsStartIndex = rowAggregationsCount ? (tableData.length - rowAggregationsCount) : null;
|
24
42
|
var html = '';
|
25
43
|
for(var i = 0; i < tableData.length; i++) {
|
26
44
|
if (i == 0) {
|
27
45
|
html += '<thead><tr>';
|
28
46
|
} else if (i == 1) {
|
29
47
|
html += '<tbody><tr>';
|
48
|
+
} else if (rowAggregationsCount && i == rowAggregationsStartIndex) {
|
49
|
+
html += '<tfoot><tr>';
|
30
50
|
} else {
|
31
51
|
html += '<tr>';
|
32
52
|
}
|
@@ -35,12 +55,14 @@ ReportsKit.Table = (function(options) {
|
|
35
55
|
if (i == 0 || j == 0) {
|
36
56
|
html += '<th>' + (tableData[i][j] || '') + '</th>';
|
37
57
|
} else {
|
38
|
-
html += '<td>' + tableData[i][j] + '</td>';
|
58
|
+
html += '<td>' + ((tableData[i][j] === null) ? '' : tableData[i][j]) + '</td>';
|
39
59
|
}
|
40
60
|
}
|
41
61
|
|
42
62
|
if (i == 0) {
|
43
63
|
html += '</tr></thead>';
|
64
|
+
} else if (i == tableData.length && rowAggregationsCount) {
|
65
|
+
html += '</tfoot></tbody>';
|
44
66
|
} else if (i == tableData.length) {
|
45
67
|
html += '</tr></tbody>';
|
46
68
|
} else {
|
@@ -52,6 +74,19 @@ ReportsKit.Table = (function(options) {
|
|
52
74
|
});
|
53
75
|
};
|
54
76
|
|
77
|
+
self.rowAggregationsCount = function(reportOptions) {
|
78
|
+
if (!reportOptions.aggregations) {
|
79
|
+
return 0;
|
80
|
+
};
|
81
|
+
var rowAggregationsCount = 0;
|
82
|
+
for(var i = 0; i < reportOptions.aggregations.length; i++) {
|
83
|
+
if (reportOptions.aggregations[i].from === 'columns') {
|
84
|
+
rowAggregationsCount += 1;
|
85
|
+
}
|
86
|
+
}
|
87
|
+
return rowAggregationsCount;
|
88
|
+
};
|
89
|
+
|
55
90
|
self.initialize(options);
|
56
91
|
|
57
92
|
return self;
|
@@ -7,6 +7,9 @@
|
|
7
7
|
width: 180px
|
8
8
|
.loading_indicator
|
9
9
|
position: absolute
|
10
|
+
margin: 5px auto
|
11
|
+
left: 0
|
12
|
+
right: 0
|
10
13
|
padding: 22px
|
11
14
|
background: url(data:image/gif;base64,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) no-repeat center
|
12
15
|
|
data/docs/dimensions.md
CHANGED
@@ -15,71 +15,64 @@ end
|
|
15
15
|
You can then use `dimensions: ['carrier']` to count the number of Flights per Carrier:
|
16
16
|
|
17
17
|
```yaml
|
18
|
-
measure:
|
19
|
-
|
20
|
-
|
21
|
-
- carrier
|
18
|
+
measure: flight
|
19
|
+
dimensions:
|
20
|
+
- carrier
|
22
21
|
```
|
23
22
|
[<img src="images/flights_by_carrier.png?raw=true" width="500" />](images/flights_by_carrier.png?raw=true)
|
24
23
|
|
25
24
|
You can also use two dimensions:
|
26
25
|
|
27
26
|
```yaml
|
28
|
-
measure:
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
- flight_at
|
27
|
+
measure: flight
|
28
|
+
dimensions:
|
29
|
+
- carrier
|
30
|
+
- flight_at
|
33
31
|
```
|
34
32
|
[<img src="images/flights_by_carrier_and_flight_at.png?raw=true" width="500" />](images/flights_by_carrier_and_flight_at.png?raw=true)
|
35
33
|
|
36
34
|
Dimensions can be configured using a string (`carrier`):
|
37
35
|
|
38
36
|
```yaml
|
39
|
-
measure:
|
40
|
-
|
41
|
-
|
42
|
-
- carrier
|
37
|
+
measure: flight
|
38
|
+
dimensions:
|
39
|
+
- carrier
|
43
40
|
```
|
44
41
|
|
45
42
|
Or, if you need to use options, you can configure them using a hash:
|
46
43
|
|
47
44
|
```yaml
|
48
|
-
measure:
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
limit: 5
|
45
|
+
measure: flight
|
46
|
+
dimensions:
|
47
|
+
- key: carrier
|
48
|
+
limit: 5
|
53
49
|
```
|
54
50
|
#### Types
|
55
51
|
|
56
52
|
##### Association
|
57
53
|
|
58
54
|
```yaml
|
59
|
-
measure:
|
60
|
-
|
61
|
-
|
62
|
-
- carrier
|
55
|
+
measure: flight
|
56
|
+
dimensions:
|
57
|
+
- carrier
|
63
58
|
```
|
64
59
|
[<img src="images/flights_by_carrier.png?raw=true" width="500" />](images/flights_by_carrier.png?raw=true)
|
65
60
|
|
66
61
|
##### Datetime Column
|
67
62
|
|
68
63
|
```yaml
|
69
|
-
measure:
|
70
|
-
|
71
|
-
|
72
|
-
- flight_at
|
64
|
+
measure: flight
|
65
|
+
dimensions:
|
66
|
+
- flight_at
|
73
67
|
```
|
74
68
|
[<img src="images/flights_by_flight_at.png?raw=true" width="500" />](images/flights_by_flight_at.png?raw=true)
|
75
69
|
|
76
70
|
##### Integer Column
|
77
71
|
|
78
72
|
```yaml
|
79
|
-
measure:
|
80
|
-
|
81
|
-
|
82
|
-
- delay
|
73
|
+
measure: flight
|
74
|
+
dimensions:
|
75
|
+
- delay
|
83
76
|
```
|
84
77
|
[<img src="images/flights_by_delay.png?raw=true" width="500" />](images/flights_by_delay.png?raw=true)
|
85
78
|
|
@@ -100,10 +93,9 @@ end
|
|
100
93
|
We can then use the `hours_delayed` dimension:
|
101
94
|
|
102
95
|
```yaml
|
103
|
-
measure:
|
104
|
-
|
105
|
-
|
106
|
-
- hours_delayed
|
96
|
+
measure: flight
|
97
|
+
dimensions:
|
98
|
+
- hours_delayed
|
107
99
|
```
|
108
100
|
[<img src="images/flights_by_hours_delayed.png?raw=true" width="500" />](images/flights_by_hours_delayed.png?raw=true)
|
109
101
|
|