aleph_analytics 0.1.0 → 0.2.0
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/CHANGELOG.md +12 -0
- data/app/assets/javascripts/angular/aleph.js.es6 +6 -0
- data/app/assets/javascripts/angular/controllers/controllers.js.es6 +1 -0
- data/app/assets/javascripts/angular/controllers/running_result/running_result_index_controller.js.es6 +56 -0
- data/app/assets/javascripts/angular/filters/filters.js.es6 +24 -6
- data/app/assets/javascripts/angular/services/services.js.es6 +10 -0
- data/app/assets/stylesheets/application.css.sass +1 -0
- data/app/assets/stylesheets/running_results.css.sass +15 -0
- data/app/controllers/admin/base_controller.rb +11 -0
- data/app/controllers/admin/running_results_controller.rb +33 -0
- data/app/models/query_execution.rb +6 -1
- data/app/models/user.rb +4 -0
- data/app/views/layouts/application.html.haml +6 -0
- data/app/views/queries/_results.html.haml +1 -1
- data/app/views/results/_show.html.haml +2 -2
- data/app/views/running_results/_index.html.haml +14 -0
- data/app/views/running_results/_index_item.html.haml +32 -0
- data/app/views/running_results/_index_sort_bar.html.haml +28 -0
- data/app/views/visualizations/_show.html.haml +2 -2
- data/config/example/redshift.yml +1 -0
- data/config/example/table_blacklist.yml +7 -0
- data/config/initializers/01_internalize_configurations.rb +4 -0
- data/config/initializers/pester.rb +2 -2
- data/config/initializers/rails_admin.rb +1 -1
- data/config/routes.rb +5 -0
- data/lib/csv_service.rb +4 -0
- data/lib/redshift_pg/connection.rb +5 -1
- data/lib/schemas/descriptor.rb +11 -1
- data/lib/schemas/paginate.rb +1 -1
- data/lib/tasks/karma.rake +1 -1
- data/lib/tasks/resque.rake +22 -0
- data/public/assets/.sprockets-manifest-61519c86aab355aa148c9e7c78293a9e.json +1 -0
- data/public/assets/angular/{aleph.js-48037f371a59a5ffaff68fd517163d50.es6 → aleph.js-4356bfc80f5b7dd3a9c9fdfacc6c8e7b.es6} +6 -0
- data/public/assets/angular/controllers/{controllers.js-94cb19ce7a5c88bfe6832a75a90b39d6.es6 → controllers.js-45fce398a9c2c371df9ffc32e8dbed84.es6} +1 -0
- data/public/assets/angular/controllers/running_result/running_result_index_controller.js-ce5ccd9d6fd27d6fea0c2c775d65a664.es6 +56 -0
- data/public/assets/angular/filters/filters.js-5d27b210896e5d5382b265ca12c96980.es6 +45 -0
- data/public/assets/angular/services/{services.js-6225c5ea24a9082506d1932d7884b53b.es6 → services.js-855551e8fa01a9a3c05115c4c9fdf7db.es6} +10 -0
- data/public/assets/{application-b990345b1178d3d77974c9ea035650ae.js → application-8bc9d85ca89b3c85e03f7d06948d2e87.js} +5 -5
- data/public/assets/{application-18bf9378371fb83c5d7714ee63887d6d.css → application-b3586e5b2749cef985bebb24246f95b6.css} +1 -1
- data/public/assets/resque_web/{application-705d1a4a22a72f773a8cd5c70ef9b284.js → application-4218536633ae4c535133fe1455d54cbc.js} +4 -4
- metadata +20 -10
- data/public/assets/.sprockets-manifest-ee5031d89f2eb0b53eb8eee944d985cf.json +0 -1
- data/public/assets/angular/filters/filters.js-48cdb16fc89337e861c9f18ad57fd9bf.es6 +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b835017996e863027ccbad3617742ffd680cfcd
|
4
|
+
data.tar.gz: 567308aed9dec7a7ab5c34ef296f1ee6276d4503
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee415ff8e6d34fd5275000d91564a19052156b7ea456a5af370cfbd546ef5d3a27760feeeb0e122fc1ea9251efb5fe5df7ed155beee3dcfff4c4a5e24e2db32f
|
7
|
+
data.tar.gz: 723ef552f7f7661ae409e4d1a6d1dc6cee7188666584a78462bdd9d893cbd17ba5c9476895cc93c9e7653fb106119bb4ab3baa8b614607c814f8a2e96cf76a2e
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,18 @@
|
|
1
1
|
# Change Log
|
2
2
|
All notable changes to this project will be documented in this file using [Semantic Versioning](http://semver.org/).
|
3
3
|
|
4
|
+
## [0.2.0] - 2017-09-12
|
5
|
+
### Features
|
6
|
+
- [Surface Running Queries](https://github.com/lumoslabs/aleph/issues/45)
|
7
|
+
- [Site wide, saved filter for schema](https://github.com/lumoslabs/aleph/issues/38)
|
8
|
+
|
9
|
+
### Fixed
|
10
|
+
- [Use trusty for travis](https://github.com/lumoslabs/aleph/issues/67)
|
11
|
+
- [Display 24-hour format](https://github.com/lumoslabs/aleph/issues/53)
|
12
|
+
- [Schema search should be able to handle numbers](https://github.com/lumoslabs/aleph/issues/59)
|
13
|
+
- [Clean up /tmp result files when query fails](https://github.com/lumoslabs/aleph/issues/37)
|
14
|
+
- [Increase schema query reties](https://github.com/lumoslabs/aleph/issues/64)
|
15
|
+
|
4
16
|
## [0.1.0] - 2017-04-27
|
5
17
|
### Features
|
6
18
|
- [Auto-complete on dot](https://github.com/lumoslabs/aleph/issues/48)
|
@@ -91,6 +91,12 @@
|
|
91
91
|
controller: 'SnippetIndexController',
|
92
92
|
controllerAs: 'snippetIdxCtrl'
|
93
93
|
})
|
94
|
+
.when('/running_results', {
|
95
|
+
title: 'RunningResults',
|
96
|
+
templateUrl: 'runningResultIndex',
|
97
|
+
controller: 'RunningResultIndexController',
|
98
|
+
controllerAs: 'runningResultsIdxCtrl'
|
99
|
+
})
|
94
100
|
.otherwise('/queries');
|
95
101
|
}]);
|
96
102
|
}(angular));
|
@@ -9,6 +9,7 @@
|
|
9
9
|
'alephControllers.queryReplController',
|
10
10
|
'alephControllers.alertIndexController',
|
11
11
|
'alephControllers.alertShowController',
|
12
|
+
'alephControllers.runningResultIndexController',
|
12
13
|
'alephControllers.snippetIndexController',
|
13
14
|
'alephControllers.singleResultShowController',
|
14
15
|
'alephServices',
|
@@ -0,0 +1,56 @@
|
|
1
|
+
!(angular => {
|
2
|
+
'use strict';
|
3
|
+
|
4
|
+
class RunningResultIndexController {
|
5
|
+
constructor($scope, ModelManager, $interval) {
|
6
|
+
this._$interval = $interval;
|
7
|
+
this._runningResultModelClasses = ModelManager.forModelName('runningResult');
|
8
|
+
this._RunningResult = this._runningResultModelClasses.modelClass();
|
9
|
+
this._RunningResults = this._runningResultModelClasses.collectionClass(this._RunningResult);
|
10
|
+
this.runningResults = new this._RunningResults();
|
11
|
+
this._pollForRunningResults();
|
12
|
+
|
13
|
+
$scope.$on('$destroy', () => {
|
14
|
+
this._$interval.cancel(this._intervalPromises);
|
15
|
+
});
|
16
|
+
|
17
|
+
// angular sorting
|
18
|
+
this.initialSortDirections = { started_at: true, author: true, duration_seconds: true, query_title: false };
|
19
|
+
}
|
20
|
+
|
21
|
+
isRepl(result) {
|
22
|
+
return !_.exists(result.item.query_id) || !_.exists(result.item.query_version_id);
|
23
|
+
}
|
24
|
+
|
25
|
+
getType(result) {
|
26
|
+
return this.isRepl(result) ? 'REPL' : 'SAVED';
|
27
|
+
}
|
28
|
+
|
29
|
+
setPredicate(predicate) {
|
30
|
+
if (predicate === this.predicate) {
|
31
|
+
this.reverse = !this.reverse;
|
32
|
+
} else {
|
33
|
+
this.predicate = predicate;
|
34
|
+
this.reverse = !!this.initialSortDirections[predicate];
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
getPredicate() {
|
39
|
+
return 'item.' + this.predicate;
|
40
|
+
}
|
41
|
+
|
42
|
+
// private methods
|
43
|
+
|
44
|
+
_pollForRunningResults() {
|
45
|
+
this.runningResults.initCollection();
|
46
|
+
this._intervalPromises = this._$interval(() => this.runningResults.initCollection(), 25000);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
RunningResultIndexController.$inject = ['$scope', 'ModelManager', '$interval'];
|
51
|
+
|
52
|
+
angular
|
53
|
+
.module('alephControllers.runningResultIndexController', ['alephServices'])
|
54
|
+
.controller('RunningResultIndexController', RunningResultIndexController);
|
55
|
+
|
56
|
+
}(angular));
|
@@ -11,17 +11,35 @@
|
|
11
11
|
})
|
12
12
|
|
13
13
|
.filter('humanReadableDuration', () => {
|
14
|
-
return (
|
14
|
+
return (originalSeconds) => {
|
15
15
|
let SECONDS_IN_HOUR = 3600;
|
16
16
|
let SECONDS_IN_MINUTE = 60;
|
17
17
|
|
18
|
-
let hours = Math.floor(
|
19
|
-
let minutes = Math.floor((
|
20
|
-
let seconds = Math.round(
|
18
|
+
let hours = Math.floor(originalSeconds / SECONDS_IN_HOUR);
|
19
|
+
let minutes = Math.floor((originalSeconds % SECONDS_IN_HOUR) / SECONDS_IN_MINUTE);
|
20
|
+
let seconds = Math.round(originalSeconds % SECONDS_IN_MINUTE);
|
21
21
|
|
22
|
-
return ((
|
23
|
-
((
|
22
|
+
return ((originalSeconds > SECONDS_IN_HOUR) ? hours + ' hours ' : '') +
|
23
|
+
((originalSeconds > SECONDS_IN_MINUTE) ? minutes + ' minutes ' : '') +
|
24
24
|
seconds + ' seconds';
|
25
25
|
};
|
26
|
+
})
|
27
|
+
|
28
|
+
.filter('runtimeDuration', () => {
|
29
|
+
return (originalSeconds) => {
|
30
|
+
let SECONDS_IN_DAY = 86400;
|
31
|
+
let SECONDS_IN_HOUR = 3600;
|
32
|
+
let SECONDS_IN_MINUTE = 60;
|
33
|
+
|
34
|
+
let days = Math.floor(originalSeconds / SECONDS_IN_DAY);
|
35
|
+
let hours = Math.floor((originalSeconds % SECONDS_IN_DAY) / SECONDS_IN_HOUR);
|
36
|
+
let minutes = Math.floor((originalSeconds % SECONDS_IN_HOUR) / SECONDS_IN_MINUTE);
|
37
|
+
let seconds = Math.round(originalSeconds % SECONDS_IN_MINUTE);
|
38
|
+
|
39
|
+
return ((originalSeconds > SECONDS_IN_DAY) ? days + ' days ' : '') +
|
40
|
+
((originalSeconds > SECONDS_IN_HOUR) ? hours + ':' : '00:') +
|
41
|
+
((originalSeconds > SECONDS_IN_MINUTE) ? minutes + ':' : '00:') +
|
42
|
+
seconds;
|
43
|
+
};
|
26
44
|
});
|
27
45
|
}(angular));
|
@@ -105,6 +105,16 @@
|
|
105
105
|
}
|
106
106
|
},
|
107
107
|
|
108
|
+
runningResult: {
|
109
|
+
//runningResult is a read only model
|
110
|
+
// no need for newItem since no need to figure out dirty awareness
|
111
|
+
newItem: {},
|
112
|
+
resource: {
|
113
|
+
path: '/running_results/:id.json',
|
114
|
+
parameters: {id: '@id'}
|
115
|
+
}
|
116
|
+
},
|
117
|
+
|
108
118
|
visualization: {
|
109
119
|
newItem: {
|
110
120
|
html_source: '',
|
@@ -0,0 +1,15 @@
|
|
1
|
+
.running-results-index
|
2
|
+
.query-body-preview
|
3
|
+
font-family: Menlo, Courier New, Courier
|
4
|
+
font-size: 9px
|
5
|
+
max-height: 100px
|
6
|
+
overflow-y: auto
|
7
|
+
|
8
|
+
.type-tag
|
9
|
+
background-color: tomato
|
10
|
+
|
11
|
+
.repl-tag-color
|
12
|
+
background-color: #5e8eae
|
13
|
+
|
14
|
+
.clipboard
|
15
|
+
margin-left: 14px
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Admin
|
2
|
+
class RunningResultsController < BaseController
|
3
|
+
|
4
|
+
RUNNING_RESULTS = <<-SQL
|
5
|
+
SELECT
|
6
|
+
title query_title,
|
7
|
+
compiled_body query_body,
|
8
|
+
TO_CHAR(results.started_at, 'YYYY-MM-DD HH24:MI:SS') started_at,
|
9
|
+
ROUND(EXTRACT(EPOCH FROM (now() - results.started_at)::INTERVAL)) duration_seconds,
|
10
|
+
users.name author,
|
11
|
+
users.role author_role,
|
12
|
+
queries.id query_id,
|
13
|
+
query_versions.id AS query_version_id,
|
14
|
+
version
|
15
|
+
FROM results
|
16
|
+
LEFT OUTER JOIN query_versions ON query_versions.id = results.query_version_id
|
17
|
+
LEFT OUTER JOIN queries ON query_versions.query_id = queries.id
|
18
|
+
INNER JOIN users ON results.owner_id = users.id
|
19
|
+
WHERE results.status = 'running'
|
20
|
+
SQL
|
21
|
+
|
22
|
+
respond_to :json
|
23
|
+
|
24
|
+
def index
|
25
|
+
respond_to do |format|
|
26
|
+
format.html
|
27
|
+
format.json do
|
28
|
+
render json: ActiveRecord::Base.connection.exec_query(RUNNING_RESULTS)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -4,6 +4,7 @@ class QueryExecution
|
|
4
4
|
|
5
5
|
def self.perform(result_id, role)
|
6
6
|
result = Result.find(result_id)
|
7
|
+
csv_service = CsvService.new(result_id)
|
7
8
|
|
8
9
|
unless Role.configured_connections.include?(role)
|
9
10
|
raise "Role '#{role}' does not have connection credentials configured."
|
@@ -31,9 +32,13 @@ class QueryExecution
|
|
31
32
|
result.mark_complete_with_count(row_count)
|
32
33
|
end
|
33
34
|
rescue *RedshiftPG::USER_ERROR_CLASSES => e
|
35
|
+
csv_service.clear_tmp_file
|
34
36
|
result.mark_failed!(e.message)
|
35
37
|
rescue => e
|
36
|
-
result
|
38
|
+
if result && csv_service
|
39
|
+
csv_service.clear_tmp_file
|
40
|
+
result.mark_failed!(e.message)
|
41
|
+
end
|
37
42
|
raise
|
38
43
|
end
|
39
44
|
end
|
data/app/models/user.rb
CHANGED
@@ -38,6 +38,8 @@
|
|
38
38
|
= render partial: 'alerts/show'
|
39
39
|
%script#snippetIndex{type: 'text/ng-template'}
|
40
40
|
= render partial: 'snippets/index'
|
41
|
+
%script#runningResultIndex{type: 'text/ng-template'}
|
42
|
+
= render partial: 'running_results/index'
|
41
43
|
%script#comments-template{type: 'text/ng-template'}
|
42
44
|
= render partial: 'queries/comments'
|
43
45
|
:javascript
|
@@ -64,6 +66,10 @@
|
|
64
66
|
%a.navbar-text{href: '/schemas', 'ng-class' => "{active: pathIncludes('schemas')}"}
|
65
67
|
%span.glyphicon.glyphicon-list.glyphicons-lg.glyphicon-med
|
66
68
|
Schemas
|
69
|
+
%a.navbar-text{href: '/running_results', 'ng-class' => "{active: pathIncludes('running_results')}",
|
70
|
+
'ng-if' => "'#{current_user.role}' == 'admin'"}
|
71
|
+
%span.glyphicon.glyphicon-plane.glyphicons-lg.glyphicon-low
|
72
|
+
Inflight
|
67
73
|
%ul.nav.navbar-nav.navbar-right
|
68
74
|
%li
|
69
75
|
- if user_signed_in?
|
@@ -11,7 +11,7 @@
|
|
11
11
|
%dd{ 'ng-bind' => 'resultsCtrl.query.item.version.version' }
|
12
12
|
%br
|
13
13
|
%dt Created:
|
14
|
-
%dd{ 'ng-bind' => 'resultsCtrl.query.item.version.created_at | date: "yyyy-MM-dd
|
14
|
+
%dd{ 'ng-bind' => 'resultsCtrl.query.item.version.created_at | date: "yyyy-MM-dd HH:mm:ss" : "UTC"' }
|
15
15
|
%br
|
16
16
|
%dt Author:
|
17
17
|
%dd{ 'ng-bind' => 'resultsCtrl.query.item.version.author_name' }
|
@@ -9,9 +9,9 @@
|
|
9
9
|
.run_duration{ 'ng-if' => 'resultCtrl.result.item.status == "complete" || resultCtrl.result.item.status == "failed"'}
|
10
10
|
Status: {{resultCtrl.result.item.status | uppercase}}
|
11
11
|
%br
|
12
|
-
Started: {{resultCtrl.result.item.started_at | date: 'yyyy-MM-dd
|
12
|
+
Started: {{resultCtrl.result.item.started_at | date: 'yyyy-MM-dd HH:mm:ss' : 'UTC'}}
|
13
13
|
%br
|
14
|
-
Ended: {{resultCtrl.result.item.completed_at | date: 'yyyy-MM-dd
|
14
|
+
Ended: {{resultCtrl.result.item.completed_at | date: 'yyyy-MM-dd HH:mm:ss' : 'UTC'}}
|
15
15
|
%br
|
16
16
|
Run duration: {{resultCtrl.result.item.run_duration | humanReadableDuration }}
|
17
17
|
%span.pull-right{ 'ng-if' => 'resultCtrl.result.item.status == "processing"' } {{resultCtrl.result.item.row_count}} rows returned so far, still processing full result and csv
|
@@ -0,0 +1,14 @@
|
|
1
|
+
.running-results-index
|
2
|
+
.row
|
3
|
+
.col-md-4
|
4
|
+
.input-group.input-group-sm
|
5
|
+
%label.input-group-addon
|
6
|
+
%i.glyphicon.glyphicon-search
|
7
|
+
%input.form-control{ 'id' => 'fulltext-search', 'ng-model' => 'searchText' }
|
8
|
+
.row.top30
|
9
|
+
.col-md-12.index-items
|
10
|
+
%ul
|
11
|
+
%li.item-header
|
12
|
+
= render partial: 'running_results/index_sort_bar'
|
13
|
+
%li.item-container{ 'ng-repeat' => 'result in runningResultsIdxCtrl.runningResults.collection | filter:searchText | orderBy:runningResultsIdxCtrl.getPredicate():runningResultsIdxCtrl.reverse' }
|
14
|
+
= render partial: 'running_results/index_item'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
.index-item
|
2
|
+
.row
|
3
|
+
.col-md-1
|
4
|
+
%span.label.type-tag{ 'ng-bind' => 'runningResultsIdxCtrl.getType(result)',
|
5
|
+
'ng-class' => "{'repl-tag-color': runningResultsIdxCtrl.isRepl(result)}"}
|
6
|
+
.col-md-2
|
7
|
+
%a{ 'ng-href' => '/queries/{{result.item.query_id}}/query_versions/{{result.item.query_version_id}}',
|
8
|
+
'ng-if' => '!runningResultsIdxCtrl.isRepl(result)' }
|
9
|
+
.ellipsis{ 'ng-bind' => 'result.item.query_title' }
|
10
|
+
%span{ 'ng-if' => 'runningResultsIdxCtrl.isRepl(result)' } -
|
11
|
+
.col-md-4
|
12
|
+
.row
|
13
|
+
.col-md-11.query-body-preview{ 'uib-popover' => '{{result.item.query_body}}',
|
14
|
+
'popover-trigger' => 'mouseenter',
|
15
|
+
'popover-popup-delay' => 1600,
|
16
|
+
'popover-placement' => 'right' }
|
17
|
+
%span{ 'ng-bind' => 'result.item.query_body'}
|
18
|
+
.col-md-1
|
19
|
+
%a.clipboard{ 'href' => '',
|
20
|
+
'text' => 'result.item.query_body',
|
21
|
+
'supported' => 'supported',
|
22
|
+
'clipboard' => '',
|
23
|
+
'uib-tooltip' => 'Copy SQL to clipboard',
|
24
|
+
'tooltip-popup-delay' => 1200,
|
25
|
+
'tooltip-placement' => 'right' }
|
26
|
+
%span.glyphicon.glyphicon-copy
|
27
|
+
.col-md-1
|
28
|
+
%span{ 'ng-bind' => 'result.item.author' }
|
29
|
+
.col-md-2
|
30
|
+
%span{ 'ng-bind' => 'result.item.started_at' }
|
31
|
+
.col-md-2
|
32
|
+
%span{ 'ng-bind' => 'result.item.duration_seconds | runtimeDuration' }
|
@@ -0,0 +1,28 @@
|
|
1
|
+
.sort-bar
|
2
|
+
.row
|
3
|
+
.col-md-1
|
4
|
+
%strong
|
5
|
+
TYPE
|
6
|
+
.col-md-2
|
7
|
+
%strong
|
8
|
+
%a{ 'ng-click' => 'runningResultsIdxCtrl.setPredicate("query_title")' }
|
9
|
+
QUERY
|
10
|
+
.glyphicon.glyphicon-sort.tiny-icon
|
11
|
+
.col-md-4
|
12
|
+
%strong
|
13
|
+
SQL BODY
|
14
|
+
.col-md-1
|
15
|
+
%strong
|
16
|
+
%a{ 'ng-click' => 'runningResultsIdxCtrl.setPredicate("author")' }
|
17
|
+
AUTHOR
|
18
|
+
.glyphicon.glyphicon-sort.tiny-icon
|
19
|
+
.col-md-2
|
20
|
+
%strong
|
21
|
+
%a{ 'ng-click' => 'runningResultsIdxCtrl.setPredicate("started_at")' }
|
22
|
+
STARTED AT
|
23
|
+
.glyphicon.glyphicon-sort.tiny-icon
|
24
|
+
.col-md-2
|
25
|
+
%strong
|
26
|
+
%a{ 'ng-click' => 'runningResultsIdxCtrl.setPredicate("duration_seconds")' }
|
27
|
+
RUN TIME
|
28
|
+
.glyphicon.glyphicon-sort.tiny-icon
|
@@ -78,6 +78,6 @@
|
|
78
78
|
%span{ 'ng-switch-when' => 'enqueued' }
|
79
79
|
Waiting to run.
|
80
80
|
%br
|
81
|
-
Enqueued at {{vizShowCtrl.sourceRenderer.latestResult().updated_at | date:'EEE yyyy-MM-dd
|
82
|
-
%span{ 'ng-switch-when' => 'running' } Running since {{vizShowCtrl.sourceRenderer.latestResult().updated_at | date:'EEE yyyy-MM-dd
|
81
|
+
Enqueued at {{vizShowCtrl.sourceRenderer.latestResult().updated_at | date:'EEE yyyy-MM-dd HH:mm:ss a' : 'UTC'}}.
|
82
|
+
%span{ 'ng-switch-when' => 'running' } Running since {{vizShowCtrl.sourceRenderer.latestResult().updated_at | date:'EEE yyyy-MM-dd HH:mm:ss a' : 'UTC'}}!
|
83
83
|
%span{ 'ng-switch-default' => true } Unknown status: "{{vizShowCtrl.sourceRenderer.latestResult().status}}"
|
data/config/example/redshift.yml
CHANGED
@@ -0,0 +1,7 @@
|
|
1
|
+
a_schema:
|
2
|
+
- a_blacklisted_table_for_schema_above
|
3
|
+
- each_entry_is_converted_to_ruby_regexep_and_matched_against_tables
|
4
|
+
- \Alets_say_i_dont_want_tables_starting_with*
|
5
|
+
another_schema:
|
6
|
+
- another_disliked_table
|
7
|
+
- ^((?!I_dont_want_any_tables_not_containing_this).)*$
|
@@ -68,3 +68,7 @@ end
|
|
68
68
|
# -------------------------------------------------
|
69
69
|
ATTRIBUTE_MAP = ingest.slurp('auth-attribute-map.yml')
|
70
70
|
File.open(Rails.root.join('config', 'attribute-map.yml'), 'w') { |f| f.write(ATTRIBUTE_MAP.to_yaml) } if ATTRIBUTE_MAP
|
71
|
+
|
72
|
+
# Schema Blacklist
|
73
|
+
# -------------------------------------------------
|
74
|
+
TABLE_BLACKLIST = ingest.slurp('table_blacklist.yml')
|
data/config/routes.rb
CHANGED
@@ -17,12 +17,17 @@ Rails.application.routes.draw do
|
|
17
17
|
resources :tags, only: [:index, :create, :destroy]
|
18
18
|
resources :alerts, only: [:show, :index, :create, :update, :destroy]
|
19
19
|
resources :snippets, only: [:show, :index, :create, :update, :destroy]
|
20
|
+
|
20
21
|
resources :query_versions, only: :show
|
21
22
|
resources :results, only: [:show, :create, :destroy]
|
22
23
|
resources :visualizations, only: [:create, :update, :destroy]
|
23
24
|
resources :schema_comments, only: [:create, :update, :destroy]
|
24
25
|
resources :result_csvs, only: :show
|
25
26
|
|
27
|
+
scope module: 'admin' do
|
28
|
+
resources :running_results, only: [:index]
|
29
|
+
end
|
30
|
+
|
26
31
|
mount ResqueWeb::Engine => "/resque_web"
|
27
32
|
|
28
33
|
root :to => 'application#index'
|
data/lib/csv_service.rb
CHANGED
@@ -12,6 +12,7 @@ module RedshiftPG
|
|
12
12
|
|
13
13
|
def initialize(config)
|
14
14
|
@config = config
|
15
|
+
@statement_timeout = config['statement_timeout']
|
15
16
|
end
|
16
17
|
|
17
18
|
def reconnect_on_failure(&block)
|
@@ -28,8 +29,11 @@ module RedshiftPG
|
|
28
29
|
end
|
29
30
|
|
30
31
|
private
|
32
|
+
|
31
33
|
def connect!
|
32
|
-
::PG.connect(pg_config)
|
34
|
+
::PG.connect(pg_config).tap do |conn|
|
35
|
+
conn.exec("SET statement_timeout to #{@statement_timeout}") if @statement_timeout
|
36
|
+
end
|
33
37
|
end
|
34
38
|
|
35
39
|
def pg_config
|
data/lib/schemas/descriptor.rb
CHANGED
@@ -70,7 +70,7 @@ module Schemas
|
|
70
70
|
end
|
71
71
|
|
72
72
|
if result
|
73
|
-
redis_store!(result.to_a)
|
73
|
+
redis_store!(filter_tables(result.to_a))
|
74
74
|
@cache = redis_retrieve
|
75
75
|
end
|
76
76
|
end
|
@@ -81,5 +81,15 @@ module Schemas
|
|
81
81
|
connection.pg_connection.exec(INFORMATION_SCHEMA_QUERY)
|
82
82
|
end
|
83
83
|
end
|
84
|
+
|
85
|
+
def filter_tables(schemas)
|
86
|
+
return schemas unless TABLE_BLACKLIST
|
87
|
+
|
88
|
+
schemas.reject do |column|
|
89
|
+
schema_blacklist = TABLE_BLACKLIST[column['table_schema']]
|
90
|
+
next unless schema_blacklist
|
91
|
+
schema_blacklist.any? { |bl_item| Regexp.new(bl_item).match(column['table_name']) }
|
92
|
+
end
|
93
|
+
end
|
84
94
|
end
|
85
95
|
end
|
data/lib/schemas/paginate.rb
CHANGED
data/lib/tasks/karma.rake
CHANGED
@@ -0,0 +1,22 @@
|
|
1
|
+
desc 'Clear pending tasks'
|
2
|
+
task 'resque:clear' => :environment do
|
3
|
+
queues = Resque.queues
|
4
|
+
queues.each do |queue_name|
|
5
|
+
puts "Clearing #{queue_name}..."
|
6
|
+
Resque.redis.del "queue:#{queue_name}"
|
7
|
+
end
|
8
|
+
|
9
|
+
# in case of scheduler - doesn't break if no scheduler module is installed
|
10
|
+
puts 'Clearing delayed...'
|
11
|
+
Resque.redis.keys('delayed:*').each do |key|
|
12
|
+
Resque.redis.del "#{key}"
|
13
|
+
end
|
14
|
+
Resque.redis.del 'delayed_queue_schedule'
|
15
|
+
|
16
|
+
puts 'Clearing failed jobs ... '
|
17
|
+
Resque::Failure.clear
|
18
|
+
|
19
|
+
puts 'Clearing stats...'
|
20
|
+
Resque.redis.set 'stat:failed', 0
|
21
|
+
Resque.redis.set 'stat:processed', 0
|
22
|
+
end
|