aleph_analytics 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/app/assets/javascripts/angular/aleph.js.es6 +6 -0
  4. data/app/assets/javascripts/angular/controllers/controllers.js.es6 +1 -0
  5. data/app/assets/javascripts/angular/controllers/running_result/running_result_index_controller.js.es6 +56 -0
  6. data/app/assets/javascripts/angular/filters/filters.js.es6 +24 -6
  7. data/app/assets/javascripts/angular/services/services.js.es6 +10 -0
  8. data/app/assets/stylesheets/application.css.sass +1 -0
  9. data/app/assets/stylesheets/running_results.css.sass +15 -0
  10. data/app/controllers/admin/base_controller.rb +11 -0
  11. data/app/controllers/admin/running_results_controller.rb +33 -0
  12. data/app/models/query_execution.rb +6 -1
  13. data/app/models/user.rb +4 -0
  14. data/app/views/layouts/application.html.haml +6 -0
  15. data/app/views/queries/_results.html.haml +1 -1
  16. data/app/views/results/_show.html.haml +2 -2
  17. data/app/views/running_results/_index.html.haml +14 -0
  18. data/app/views/running_results/_index_item.html.haml +32 -0
  19. data/app/views/running_results/_index_sort_bar.html.haml +28 -0
  20. data/app/views/visualizations/_show.html.haml +2 -2
  21. data/config/example/redshift.yml +1 -0
  22. data/config/example/table_blacklist.yml +7 -0
  23. data/config/initializers/01_internalize_configurations.rb +4 -0
  24. data/config/initializers/pester.rb +2 -2
  25. data/config/initializers/rails_admin.rb +1 -1
  26. data/config/routes.rb +5 -0
  27. data/lib/csv_service.rb +4 -0
  28. data/lib/redshift_pg/connection.rb +5 -1
  29. data/lib/schemas/descriptor.rb +11 -1
  30. data/lib/schemas/paginate.rb +1 -1
  31. data/lib/tasks/karma.rake +1 -1
  32. data/lib/tasks/resque.rake +22 -0
  33. data/public/assets/.sprockets-manifest-61519c86aab355aa148c9e7c78293a9e.json +1 -0
  34. data/public/assets/angular/{aleph.js-48037f371a59a5ffaff68fd517163d50.es6 → aleph.js-4356bfc80f5b7dd3a9c9fdfacc6c8e7b.es6} +6 -0
  35. data/public/assets/angular/controllers/{controllers.js-94cb19ce7a5c88bfe6832a75a90b39d6.es6 → controllers.js-45fce398a9c2c371df9ffc32e8dbed84.es6} +1 -0
  36. data/public/assets/angular/controllers/running_result/running_result_index_controller.js-ce5ccd9d6fd27d6fea0c2c775d65a664.es6 +56 -0
  37. data/public/assets/angular/filters/filters.js-5d27b210896e5d5382b265ca12c96980.es6 +45 -0
  38. data/public/assets/angular/services/{services.js-6225c5ea24a9082506d1932d7884b53b.es6 → services.js-855551e8fa01a9a3c05115c4c9fdf7db.es6} +10 -0
  39. data/public/assets/{application-b990345b1178d3d77974c9ea035650ae.js → application-8bc9d85ca89b3c85e03f7d06948d2e87.js} +5 -5
  40. data/public/assets/{application-18bf9378371fb83c5d7714ee63887d6d.css → application-b3586e5b2749cef985bebb24246f95b6.css} +1 -1
  41. data/public/assets/resque_web/{application-705d1a4a22a72f773a8cd5c70ef9b284.js → application-4218536633ae4c535133fe1455d54cbc.js} +4 -4
  42. metadata +20 -10
  43. data/public/assets/.sprockets-manifest-ee5031d89f2eb0b53eb8eee944d985cf.json +0 -1
  44. 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: 2956af7f2c50f50dd57e7c7cd8d719a1a3b01257
4
- data.tar.gz: f1b92773a08c484d40dfdbedb125b2702b217481
3
+ metadata.gz: 2b835017996e863027ccbad3617742ffd680cfcd
4
+ data.tar.gz: 567308aed9dec7a7ab5c34ef296f1ee6276d4503
5
5
  SHA512:
6
- metadata.gz: 5fc0a70b14b26d7d0a013e194a84163dbf464d2534e97d91365436e7249b3b802f101f5dadcd433fa1de16650c65367b644f4e0aed1fc7cb5790cf8d7ebbd7eb
7
- data.tar.gz: a673d808104cc7bcfdd4905c24e05991450322cfb9e21a70b3677e326f3f3d9f74d40d22e690f2ed638815416ac770f430596cbc1e48ce9ea608a43eb55f1670
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 (original_seconds) => {
14
+ return (originalSeconds) => {
15
15
  let SECONDS_IN_HOUR = 3600;
16
16
  let SECONDS_IN_MINUTE = 60;
17
17
 
18
- let hours = Math.floor(original_seconds / SECONDS_IN_HOUR);
19
- let minutes = Math.floor((original_seconds % SECONDS_IN_HOUR) / SECONDS_IN_MINUTE);
20
- let seconds = Math.round(original_seconds % SECONDS_IN_MINUTE);
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 ((original_seconds > SECONDS_IN_HOUR) ? hours + ' hours ' : '') +
23
- ((original_seconds > SECONDS_IN_MINUTE) ? minutes + ' minutes ' : '') +
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: '',
@@ -21,6 +21,7 @@
21
21
  @import 'repl'
22
22
  @import 'alerts'
23
23
  @import 'snippets'
24
+ @import 'running_results'
24
25
  @import 'visualizations'
25
26
  @import 'sidebar'
26
27
  @import 'comments'
@@ -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,11 @@
1
+ module Admin
2
+ class BaseController < ApplicationController
3
+ before_filter :verify_admin
4
+
5
+ private
6
+
7
+ def verify_admin
8
+ redirect_to root_url unless current_user.try(:admin?)
9
+ end
10
+ end
11
+ end
@@ -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.mark_failed!(e.message) if 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
@@ -16,4 +16,8 @@ class User < ActiveRecord::Base
16
16
  def self.guest_user
17
17
  where(email: 'guest-user@test.com', role: Role::ADMIN_ROLE, name: 'Guest').first_or_create
18
18
  end
19
+
20
+ def admin?
21
+ role == Role::ADMIN_ROLE
22
+ end
19
23
  end
@@ -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
  &nbsp;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
+ &nbsp;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 hh:mm:ss" : "UTC"' }
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 hh:mm:ss' : 'UTC'}}
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 hh:mm:ss' : 'UTC'}}
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 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'}}!
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}}"
@@ -5,6 +5,7 @@ redshift_common: &redshift_common
5
5
  host: warehouse.pub.yourcompany.com
6
6
  database: warehouse
7
7
  port: '1234'
8
+ statement_timeout: 1800000
8
9
 
9
10
  production:
10
11
  <<: *redshift_common
@@ -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')
@@ -2,8 +2,8 @@ Pester.configure do |c|
2
2
  c.logger = Rails.logger
3
3
 
4
4
  c.environments[:schema_refresh] = {
5
- delay_interval: 1,
6
- max_attempts: 12,
5
+ delay_interval: 3,
6
+ max_attempts: 15,
7
7
  on_retry: Pester::Behaviors::Sleep::Linear
8
8
  }
9
9
 
@@ -1,6 +1,6 @@
1
1
  RailsAdmin.config do |config|
2
2
  config.authenticate_with do
3
- redirect_to main_app.root_path unless current_user.role == Role::ADMIN_ROLE
3
+ redirect_to main_app.root_path unless current_user.try(:admin?)
4
4
  end
5
5
 
6
6
  config.included_models = ['User']
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
@@ -9,4 +9,8 @@ class CsvService
9
9
  @service_impl = CsvHelper::Local.new(result_id)
10
10
  end
11
11
  end
12
+
13
+ def clear_tmp_file
14
+ File.delete(filepath) if File.exist?(filepath)
15
+ end
12
16
  end
@@ -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
@@ -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
@@ -3,7 +3,7 @@ module Schemas
3
3
  protected
4
4
 
5
5
  def searched(items, search)
6
- search = expand_dot_notation(search) if /\A[A-z]+\.[A-z]+\z/ =~ search
6
+ search = expand_dot_notation(search) if /\A[A-z0-9]+\.[A-z0-9]+\z/ =~ search
7
7
  super(items, search)
8
8
  end
9
9
 
data/lib/tasks/karma.rake CHANGED
@@ -16,7 +16,7 @@ namespace :karma do
16
16
  f.flush
17
17
  pass = system "karma #{command} #{f.path} #{args}"
18
18
  end
19
- exit(pass)
19
+ exit(!!pass)
20
20
  end
21
21
 
22
22
  def application_spec_files
@@ -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