kanaui 0.6.0 → 2.1.1
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.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/Rakefile +4 -5
- data/app/assets/javascripts/application.js +1 -0
- data/app/assets/javascripts/kanaui/kiddo/kiddo_initialize.js +1 -1
- data/app/assets/javascripts/kanaui/reports.dataTables.js +74 -2
- data/app/assets/stylesheets/application.css +1 -0
- data/app/assets/stylesheets/kanaui/reports.css +66 -2
- data/app/controllers/kanaui/dashboard_controller.rb +28 -11
- data/app/controllers/kanaui/engine_controller.rb +41 -6
- data/app/controllers/kanaui/reports_controller.rb +15 -12
- data/app/controllers/kanaui/settings_controller.rb +13 -0
- data/app/helpers/kanaui/application_helper.rb +2 -2
- data/app/helpers/kanaui/dashboard_helper.rb +7 -5
- data/app/views/kanaui/dashboard/index.html.erb +153 -125
- data/app/views/kanaui/reports/_form.html.erb +30 -16
- data/app/views/kanaui/reports/_reports_table.html.erb +22 -0
- data/app/views/kanaui/settings/index.html.erb +19 -0
- data/config/routes.rb +11 -8
- data/lib/kanaui.rb +11 -10
- data/lib/kanaui/engine.rb +2 -0
- data/lib/kanaui/version.rb +3 -1
- data/lib/tasks/kanaui_tasks.rake +1 -0
- data/test/dummy/app/assets/config/manifest.js +1 -0
- data/test/dummy/config/initializers/killbill_client.rb +3 -3
- data/test/functional/kanaui/tests_controller_test.rb +2 -0
- data/test/integration/navigation_test.rb +2 -2
- data/test/kanaui_test.rb +3 -1
- data/test/test_helper.rb +7 -7
- data/test/unit/helpers/kanaui/tests_helper_test.rb +2 -0
- metadata +109 -63
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b97ea14f934efc662bd48c78371bb3bbaf57a347
|
4
|
+
data.tar.gz: e3b86efa67ff817e61cd181654d92fa1ba1fad3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bf5d0c1847d69ca090ea670cd9731ecaeac658bac5b5a2fd2cf7dea1a463a9fa138a5c32ffd8db01f5a69a3d50efedf1a492b4c9705cfe6bf13ab1e2831654f
|
7
|
+
data.tar.gz: 62cd8b7055b53dce95b8abfe0f309a7d4cd84cb9ed18da9bb700341c38191b743b80ae852feb2821954e2aae26675fbefec4192a72d41371de01e90e4f982774
|
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
begin
|
3
5
|
require 'bundler/setup'
|
4
6
|
rescue LoadError
|
@@ -20,11 +22,9 @@ RDoc::Task.new(:rdoc) do |rdoc|
|
|
20
22
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
23
|
end
|
22
24
|
|
23
|
-
APP_RAKEFILE = File.expand_path(
|
25
|
+
APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
|
24
26
|
load 'rails/tasks/engine.rake'
|
25
27
|
|
26
|
-
|
27
|
-
|
28
28
|
Bundler::GemHelper.install_tasks
|
29
29
|
|
30
30
|
require 'rake/testtask'
|
@@ -36,5 +36,4 @@ Rake::TestTask.new(:test) do |t|
|
|
36
36
|
t.verbose = false
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
|
-
task :default => :test
|
39
|
+
task default: :test
|
@@ -12,6 +12,7 @@
|
|
12
12
|
//= require twitter/bootstrap
|
13
13
|
//= require dataTables/jquery.dataTables
|
14
14
|
//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
|
15
|
+
//= require dataTables/extras/dataTables.colVis
|
15
16
|
//= require d3
|
16
17
|
//= require bootstrap-datepicker
|
17
18
|
//= require jquery.spin
|
@@ -40,16 +40,88 @@ ReportsDataTables.prototype.buildTable = function(data, wrapper) {
|
|
40
40
|
}
|
41
41
|
|
42
42
|
var aoColumns = [];
|
43
|
+
var columnsVisible = [];
|
43
44
|
for (var i in data['header']) {
|
44
|
-
|
45
|
+
var isVisible = isColumnVisible(data['header'][i]);
|
46
|
+
aoColumns.push({ "sTitle": data['header'][i], "name": data['header'][i], "visible": isVisible })
|
47
|
+
if (isVisible) {
|
48
|
+
columnsVisible.push(data['header'][i]);
|
49
|
+
}
|
45
50
|
}
|
46
51
|
|
47
52
|
dataTable.dataTable({
|
48
53
|
"aaData": aaData,
|
49
|
-
"aoColumns": aoColumns
|
54
|
+
"aoColumns": aoColumns,
|
55
|
+
"scrollX": true,
|
56
|
+
"sDom": 'C<"clear">lfrtip'
|
50
57
|
});
|
58
|
+
|
59
|
+
dataTable.on( 'column-visibility.dt', function ( e, settings, column, state ) {
|
60
|
+
setColumnVisible(settings.aoColumns[column].name, state);
|
61
|
+
});
|
62
|
+
|
63
|
+
$("#copy-url").click(function(e){
|
64
|
+
var pathPlusParams = $(this).data("reports-path");
|
65
|
+
var sPageURL = decodeURIComponent(pathPlusParams.substring(1)).split('?');
|
66
|
+
var params = sPageURL[1].split('&');
|
67
|
+
|
68
|
+
var columnsVisible = $("#visible-table-columns").val();
|
69
|
+
var placeholder = $("#url-placeholder");
|
70
|
+
|
71
|
+
var urlToShare = window.location.origin + "/" + sPageURL[0] + "?";
|
72
|
+
for (var i in params) {
|
73
|
+
var keyValue = params[i].split('=');
|
74
|
+
if (keyValue[0] == 'columns') {
|
75
|
+
continue;
|
76
|
+
}
|
77
|
+
urlToShare += "&" + params[i];
|
78
|
+
}
|
79
|
+
|
80
|
+
placeholder.val(urlToShare + "&columns=" + columnsVisible);
|
81
|
+
placeholder.removeClass("hidden");
|
82
|
+
placeholder.select();
|
83
|
+
|
84
|
+
document.execCommand("Copy");
|
85
|
+
placeholder.addClass("hidden");
|
86
|
+
alert("URL copied into the clipboard!")
|
87
|
+
});
|
88
|
+
|
89
|
+
$("#visible-table-columns").val(columnsVisible.join());
|
51
90
|
}
|
52
91
|
|
53
92
|
ReportsDataTables.prototype.buildCSVURL = function(position) {
|
54
93
|
return this.reports.buildDataURL(position, 'csv');
|
55
94
|
}
|
95
|
+
function setColumnVisible(column, isVisible) {
|
96
|
+
var columnsVisible = $("#visible-table-columns").val();
|
97
|
+
if (columnsVisible == undefined || columnsVisible == null || columnsVisible.length == 0) {
|
98
|
+
columnsVisible = [];
|
99
|
+
} else {
|
100
|
+
columnsVisible = (columnsVisible).split(",");
|
101
|
+
}
|
102
|
+
|
103
|
+
var columnIndex = columnsVisible.indexOf(column);
|
104
|
+
if (isVisible && columnIndex == -1) {
|
105
|
+
columnsVisible.push(column);
|
106
|
+
} else if (!isVisible && columnIndex != -1) {
|
107
|
+
columnsVisible.splice(columnIndex, 1);
|
108
|
+
}
|
109
|
+
|
110
|
+
$("#visible-table-columns").val(columnsVisible.join());
|
111
|
+
}
|
112
|
+
|
113
|
+
function isColumnVisible(column) {
|
114
|
+
var columnsVisible = $("#visible-table-columns").val();
|
115
|
+
if ((columnsVisible == undefined || columnsVisible == null || columnsVisible.length == 0) && column != 'tenant_record_id') {
|
116
|
+
return true;
|
117
|
+
}
|
118
|
+
columnsVisible = (columnsVisible).split(",");
|
119
|
+
|
120
|
+
for (var i in columnsVisible) {
|
121
|
+
if (columnsVisible[i] == column) {
|
122
|
+
return true;
|
123
|
+
}
|
124
|
+
}
|
125
|
+
|
126
|
+
return false;
|
127
|
+
}
|
@@ -6,6 +6,7 @@
|
|
6
6
|
*= require bootstrap-datepicker3
|
7
7
|
*= require dataTables/jquery.dataTables
|
8
8
|
*= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
|
9
|
+
*= require dataTables/extras/dataTables.colVis
|
9
10
|
*= require font-awesome
|
10
11
|
*= require bootstrap_and_overrides
|
11
12
|
*= require kanaui/kanaui
|
@@ -1,5 +1,10 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
#reports-table th, #reports-table td,
|
2
|
+
#dataTable-api_wrapper th, #dataTable-api_wrapper td {
|
3
|
+
white-space: nowrap;
|
4
|
+
}
|
5
|
+
|
6
|
+
.sql-query {
|
7
|
+
word-break: break-word;
|
3
8
|
}
|
4
9
|
|
5
10
|
.axis path,
|
@@ -92,3 +97,62 @@
|
|
92
97
|
.grid path{
|
93
98
|
stroke-width: 0;
|
94
99
|
}
|
100
|
+
|
101
|
+
button.ColVis_Button, button.ColVis_Button:hover {
|
102
|
+
background: inherit;
|
103
|
+
box-shadow: unset;
|
104
|
+
text-transform: none;
|
105
|
+
color: #333 !important;
|
106
|
+
border-color: #adadad;
|
107
|
+
}
|
108
|
+
|
109
|
+
ul.ColVis_collection {
|
110
|
+
background-color: #f3f3f3;
|
111
|
+
}
|
112
|
+
|
113
|
+
ul.ColVis_collection li, ul.ColVis_collection li:hover{
|
114
|
+
border: none;
|
115
|
+
box-shadow: unset;
|
116
|
+
background: transparent;
|
117
|
+
}
|
118
|
+
|
119
|
+
button.ColVis_Button:hover, ul.ColVis_collection li:hover {
|
120
|
+
background-color: #e6e6e6;
|
121
|
+
border-color: #adadad;
|
122
|
+
}
|
123
|
+
|
124
|
+
.dataTables_wrapper .dataTables_paginate .paginate_button {
|
125
|
+
padding: 0.25em 0.5em;
|
126
|
+
}
|
127
|
+
|
128
|
+
.flex-panel {
|
129
|
+
display: -webkit-flex;
|
130
|
+
display: flex;
|
131
|
+
-webkit-flex-direction: row;
|
132
|
+
flex-direction: row;
|
133
|
+
}
|
134
|
+
|
135
|
+
.flex-inner-left-panel {
|
136
|
+
-ms-flex: 1 .5 0%;
|
137
|
+
-webkit-flex: 1 .5 0%;
|
138
|
+
flex: 1 .5 0%;
|
139
|
+
}
|
140
|
+
|
141
|
+
.flex-inner-left-panel ul li {
|
142
|
+
white-space: nowrap;
|
143
|
+
}
|
144
|
+
|
145
|
+
.flex-inner-right-panel {
|
146
|
+
-ms-flex: .5 1 70%;
|
147
|
+
-webkit-flex: .5 1 70%;
|
148
|
+
flex: .5 1 70%;
|
149
|
+
overflow: scroll;
|
150
|
+
}
|
151
|
+
|
152
|
+
#date-picker .form-group {
|
153
|
+
height: 34px;
|
154
|
+
}
|
155
|
+
|
156
|
+
#table-tools {
|
157
|
+
padding-bottom: 10px;
|
158
|
+
}
|
@@ -1,6 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Kanaui
|
2
4
|
class DashboardController < Kanaui::EngineController
|
3
|
-
|
4
5
|
#
|
5
6
|
# Load the dashboard by rendering the view and executing the javascript that will call
|
6
7
|
# the reports and available_reports endpoints below.
|
@@ -20,16 +21,19 @@ module Kanaui
|
|
20
21
|
|
21
22
|
query = build_slice_and_dice_query
|
22
23
|
|
24
|
+
# get columns visibility from query parameters to be used by tables
|
25
|
+
@visible_columns = params[:columns]
|
26
|
+
|
23
27
|
# Redirect also in case the dates have been updated to avoid any confusion in the view
|
24
28
|
if query.present? || params[:start_date].blank? || params[:end_date].blank?
|
25
|
-
# TODO Make metrics configurable
|
29
|
+
# TODO: Make metrics configurable
|
26
30
|
name = query.present? ? "#{params[:name]}#{query}^metric:count" : params[:name]
|
27
|
-
query_params = {:
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
query_params = { start_date: @start_date,
|
32
|
+
end_date: @end_date,
|
33
|
+
name: name,
|
34
|
+
smooth: params[:smooth],
|
35
|
+
sql_only: params[:sql_only],
|
36
|
+
format: params[:format] }
|
33
37
|
|
34
38
|
# Test only
|
35
39
|
query_params[:fake] = params[:fake] unless params[:fake].blank?
|
@@ -44,7 +48,7 @@ module Kanaui
|
|
44
48
|
# Not used anymore as reports are pulled from index
|
45
49
|
def available_reports
|
46
50
|
available_reports = Kanaui::DashboardHelper::DashboardApi.available_reports(options_for_klient)
|
47
|
-
render :
|
51
|
+
render json: available_reports
|
48
52
|
end
|
49
53
|
|
50
54
|
def reports
|
@@ -63,7 +67,18 @@ module Kanaui
|
|
63
67
|
else
|
64
68
|
reports = raw_reports
|
65
69
|
end
|
66
|
-
|
70
|
+
respond_to do |fmt|
|
71
|
+
fmt.csv do
|
72
|
+
filename = params[:name]
|
73
|
+
unless params[:start_date].blank?
|
74
|
+
filename += "_#{params[:start_date]}"
|
75
|
+
filename += "-#{params[:end_date]}" unless params[:end_date].blank?
|
76
|
+
end
|
77
|
+
filename += '.csv'
|
78
|
+
send_data(raw_reports, filename: filename)
|
79
|
+
end
|
80
|
+
fmt.all { render json: reports }
|
81
|
+
end
|
67
82
|
end
|
68
83
|
|
69
84
|
private
|
@@ -114,6 +129,7 @@ module Kanaui
|
|
114
129
|
filter_query = ''
|
115
130
|
filters.each do |k, v|
|
116
131
|
next if v.blank?
|
132
|
+
|
117
133
|
filter_query << '%26' unless filter_query.blank?
|
118
134
|
filter_query << "(#{k}=#{v.join("|#{k}=")})"
|
119
135
|
end
|
@@ -121,7 +137,8 @@ module Kanaui
|
|
121
137
|
|
122
138
|
groups.each do |k, v|
|
123
139
|
next if v.blank?
|
124
|
-
|
140
|
+
|
141
|
+
# TODO: Make "no other" configurable
|
125
142
|
query << "^dimension:#{k}(#{v.join('|')}|-)"
|
126
143
|
end
|
127
144
|
|
@@ -1,6 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Kanaui
|
2
4
|
class EngineController < ApplicationController
|
3
|
-
|
4
5
|
layout :get_layout
|
5
6
|
|
6
7
|
def get_layout
|
@@ -17,12 +18,46 @@ module Kanaui
|
|
17
18
|
def options_for_klient
|
18
19
|
user = current_tenant_user
|
19
20
|
{
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
username: user[:username],
|
22
|
+
password: user[:password],
|
23
|
+
session_id: user[:session_id],
|
24
|
+
api_key: user[:api_key],
|
25
|
+
api_secret: user[:api_secret]
|
25
26
|
}
|
26
27
|
end
|
28
|
+
|
29
|
+
rescue_from(KillBillClient::API::ResponseError) do |killbill_exception|
|
30
|
+
flash[:error] = "Error while communicating with the Kill Bill server: #{as_string(killbill_exception)}"
|
31
|
+
redirect_to dashboard_index_path
|
32
|
+
end
|
33
|
+
|
34
|
+
def as_string(e)
|
35
|
+
if e.is_a?(KillBillClient::API::ResponseError)
|
36
|
+
"Error #{e.response.code}: #{as_string_from_response(e.response.body)}"
|
37
|
+
else
|
38
|
+
log_rescue_error(e)
|
39
|
+
e.message
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def log_rescue_error(error)
|
44
|
+
Rails.logger.warn "#{error.class} #{error}. #{error.backtrace.join("\n")}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def as_string_from_response(response)
|
48
|
+
error_message = response
|
49
|
+
begin
|
50
|
+
# BillingExceptionJson?
|
51
|
+
error_message = JSON.parse response
|
52
|
+
rescue StandardError => _e
|
53
|
+
end
|
54
|
+
|
55
|
+
if error_message.respond_to?(:[]) && error_message['message'].present?
|
56
|
+
# Likely BillingExceptionJson
|
57
|
+
error_message = error_message['message']
|
58
|
+
end
|
59
|
+
# Limit the error size to avoid ActionDispatch::Cookies::CookieOverflow
|
60
|
+
error_message[0..1000]
|
61
|
+
end
|
27
62
|
end
|
28
63
|
end
|
@@ -1,6 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Kanaui
|
2
4
|
class ReportsController < Kanaui::EngineController
|
3
|
-
|
4
5
|
def index
|
5
6
|
@reports = JSON.parse(Kanaui::DashboardHelper::DashboardApi.available_reports(options_for_klient)).map(&:deep_symbolize_keys)
|
6
7
|
end
|
@@ -13,7 +14,7 @@ module Kanaui
|
|
13
14
|
Kanaui::DashboardHelper::DashboardApi.create_report(report_from_params.to_json, options_for_klient)
|
14
15
|
|
15
16
|
flash[:notice] = 'Report successfully created'
|
16
|
-
redirect_to :
|
17
|
+
redirect_to action: :index
|
17
18
|
end
|
18
19
|
|
19
20
|
def edit
|
@@ -26,34 +27,36 @@ module Kanaui
|
|
26
27
|
Kanaui::DashboardHelper::DashboardApi.update_report(params.require(:id), report_from_params.to_json, options_for_klient)
|
27
28
|
|
28
29
|
flash[:notice] = 'Report successfully updated'
|
29
|
-
redirect_to :
|
30
|
+
redirect_to action: :index
|
30
31
|
end
|
31
32
|
|
32
33
|
def refresh
|
33
34
|
Kanaui::DashboardHelper::DashboardApi.refresh_report(params.require(:id), options_for_klient)
|
34
35
|
|
35
36
|
flash[:notice] = 'Report refresh successfully scheduled'
|
36
|
-
redirect_to :
|
37
|
+
redirect_to action: :index
|
37
38
|
end
|
38
39
|
|
39
40
|
def destroy
|
40
41
|
Kanaui::DashboardHelper::DashboardApi.delete_report(params.require(:id), options_for_klient)
|
41
42
|
|
42
43
|
flash[:notice] = 'Report successfully deleted'
|
43
|
-
redirect_to :
|
44
|
+
redirect_to action: :index
|
44
45
|
end
|
45
46
|
|
46
47
|
private
|
47
48
|
|
48
49
|
def report_from_params
|
49
50
|
{
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
51
|
+
reportName: params[:report_name],
|
52
|
+
reportPrettyName: params[:report_pretty_name],
|
53
|
+
reportType: params[:report_type],
|
54
|
+
sourceTableName: params[:source_table_name],
|
55
|
+
sourceName: params[:source_name],
|
56
|
+
sourceQuery: params[:source_query],
|
57
|
+
refreshProcedureName: params[:refresh_procedure_name],
|
58
|
+
refreshFrequency: params[:refresh_frequency].presence,
|
59
|
+
refreshHourOfDayGmt: params[:refresh_hour_of_day_gmt]
|
57
60
|
}
|
58
61
|
end
|
59
62
|
end
|