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.
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