sql-jarvis 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/CHANGELOG.md +228 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +775 -0
  7. data/Rakefile +1 -0
  8. data/app/assets/fonts/blazer/glyphicons-halflings-regular.eot +0 -0
  9. data/app/assets/fonts/blazer/glyphicons-halflings-regular.svg +288 -0
  10. data/app/assets/fonts/blazer/glyphicons-halflings-regular.ttf +0 -0
  11. data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff +0 -0
  12. data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff2 +0 -0
  13. data/app/assets/javascripts/blazer/Chart.js +14145 -0
  14. data/app/assets/javascripts/blazer/Sortable.js +1144 -0
  15. data/app/assets/javascripts/blazer/ace.js +6 -0
  16. data/app/assets/javascripts/blazer/ace/ace.js +11 -0
  17. data/app/assets/javascripts/blazer/ace/ext-language_tools.js +5 -0
  18. data/app/assets/javascripts/blazer/ace/mode-sql.js +1 -0
  19. data/app/assets/javascripts/blazer/ace/snippets/sql.js +1 -0
  20. data/app/assets/javascripts/blazer/ace/snippets/text.js +1 -0
  21. data/app/assets/javascripts/blazer/ace/theme-twilight.js +1 -0
  22. data/app/assets/javascripts/blazer/application.js +79 -0
  23. data/app/assets/javascripts/blazer/bootstrap.js +2366 -0
  24. data/app/assets/javascripts/blazer/chartkick.js +1693 -0
  25. data/app/assets/javascripts/blazer/daterangepicker.js +1505 -0
  26. data/app/assets/javascripts/blazer/fuzzysearch.js +24 -0
  27. data/app/assets/javascripts/blazer/highlight.pack.js +1 -0
  28. data/app/assets/javascripts/blazer/jquery.js +10308 -0
  29. data/app/assets/javascripts/blazer/jquery.stickytableheaders.js +263 -0
  30. data/app/assets/javascripts/blazer/jquery_ujs.js +469 -0
  31. data/app/assets/javascripts/blazer/moment-timezone.js +1007 -0
  32. data/app/assets/javascripts/blazer/moment.js +3043 -0
  33. data/app/assets/javascripts/blazer/queries.js +110 -0
  34. data/app/assets/javascripts/blazer/routes.js +23 -0
  35. data/app/assets/javascripts/blazer/selectize.js +3667 -0
  36. data/app/assets/javascripts/blazer/stupidtable.js +114 -0
  37. data/app/assets/javascripts/blazer/vue.js +7515 -0
  38. data/app/assets/stylesheets/blazer/application.css +198 -0
  39. data/app/assets/stylesheets/blazer/bootstrap.css.erb +6202 -0
  40. data/app/assets/stylesheets/blazer/daterangepicker-bs3.css +375 -0
  41. data/app/assets/stylesheets/blazer/github.css +125 -0
  42. data/app/assets/stylesheets/blazer/selectize.default.css +387 -0
  43. data/app/controllers/blazer/base_controller.rb +103 -0
  44. data/app/controllers/blazer/checks_controller.rb +56 -0
  45. data/app/controllers/blazer/dashboards_controller.rb +105 -0
  46. data/app/controllers/blazer/queries_controller.rb +325 -0
  47. data/app/helpers/blazer/base_helper.rb +57 -0
  48. data/app/mailers/blazer/check_mailer.rb +27 -0
  49. data/app/models/blazer/audit.rb +6 -0
  50. data/app/models/blazer/check.rb +95 -0
  51. data/app/models/blazer/connection.rb +5 -0
  52. data/app/models/blazer/dashboard.rb +13 -0
  53. data/app/models/blazer/dashboard_query.rb +9 -0
  54. data/app/models/blazer/query.rb +31 -0
  55. data/app/models/blazer/record.rb +5 -0
  56. data/app/views/blazer/_nav.html.erb +16 -0
  57. data/app/views/blazer/_variables.html.erb +102 -0
  58. data/app/views/blazer/check_mailer/failing_checks.html.erb +6 -0
  59. data/app/views/blazer/check_mailer/state_change.html.erb +47 -0
  60. data/app/views/blazer/checks/_form.html.erb +71 -0
  61. data/app/views/blazer/checks/edit.html.erb +1 -0
  62. data/app/views/blazer/checks/index.html.erb +40 -0
  63. data/app/views/blazer/checks/new.html.erb +1 -0
  64. data/app/views/blazer/dashboards/_form.html.erb +76 -0
  65. data/app/views/blazer/dashboards/edit.html.erb +1 -0
  66. data/app/views/blazer/dashboards/new.html.erb +1 -0
  67. data/app/views/blazer/dashboards/show.html.erb +47 -0
  68. data/app/views/blazer/queries/_form.html.erb +240 -0
  69. data/app/views/blazer/queries/edit.html.erb +2 -0
  70. data/app/views/blazer/queries/home.html.erb +152 -0
  71. data/app/views/blazer/queries/new.html.erb +2 -0
  72. data/app/views/blazer/queries/run.html.erb +163 -0
  73. data/app/views/blazer/queries/schema.html.erb +18 -0
  74. data/app/views/blazer/queries/show.html.erb +73 -0
  75. data/app/views/layouts/blazer/application.html.erb +24 -0
  76. data/blazer.gemspec +26 -0
  77. data/config/routes.rb +16 -0
  78. data/lib/blazer.rb +185 -0
  79. data/lib/blazer/adapters/athena_adapter.rb +128 -0
  80. data/lib/blazer/adapters/base_adapter.rb +53 -0
  81. data/lib/blazer/adapters/bigquery_adapter.rb +67 -0
  82. data/lib/blazer/adapters/drill_adapter.rb +28 -0
  83. data/lib/blazer/adapters/elasticsearch_adapter.rb +49 -0
  84. data/lib/blazer/adapters/mongodb_adapter.rb +39 -0
  85. data/lib/blazer/adapters/presto_adapter.rb +45 -0
  86. data/lib/blazer/adapters/sql_adapter.rb +182 -0
  87. data/lib/blazer/data_source.rb +193 -0
  88. data/lib/blazer/detect_anomalies.R +19 -0
  89. data/lib/blazer/engine.rb +47 -0
  90. data/lib/blazer/result.rb +170 -0
  91. data/lib/blazer/run_statement.rb +40 -0
  92. data/lib/blazer/run_statement_job.rb +21 -0
  93. data/lib/blazer/version.rb +3 -0
  94. data/lib/generators/blazer/install_generator.rb +39 -0
  95. data/lib/generators/blazer/templates/config.yml +62 -0
  96. data/lib/generators/blazer/templates/install.rb +45 -0
  97. data/lib/tasks/blazer.rake +10 -0
  98. metadata +211 -0
@@ -0,0 +1,57 @@
1
+ module Blazer
2
+ module BaseHelper
3
+ def blazer_title(title = nil)
4
+ if title
5
+ content_for(:title) { title }
6
+ else
7
+ content_for?(:title) ? content_for(:title) : nil
8
+ end
9
+ end
10
+
11
+ BLAZER_URL_REGEX = /\Ahttps?:\/\/[\S]+\z/
12
+ BLAZER_IMAGE_EXT = %w[png jpg jpeg gif]
13
+
14
+ def blazer_format_value(key, value)
15
+ if value.is_a?(Integer) && !key.to_s.end_with?("id") && !key.to_s.start_with?("id")
16
+ number_with_delimiter(value)
17
+ elsif value =~ BLAZER_URL_REGEX
18
+ # see if image or link
19
+ if Blazer.images && (key.include?("image") || BLAZER_IMAGE_EXT.include?(value.split(".").last.split("?").first.try(:downcase)))
20
+ link_to value, target: "_blank" do
21
+ image_tag value, referrerpolicy: "no-referrer"
22
+ end
23
+ else
24
+ link_to value, value, target: "_blank"
25
+ end
26
+ else
27
+ value
28
+ end
29
+ end
30
+
31
+ def blazer_maps?
32
+ Blazer.mapbox_access_token.present?
33
+ end
34
+
35
+ def blazer_js_var(name, value)
36
+ "var #{name} = #{blazer_json_escape(value.to_json(root: false))};".html_safe
37
+ end
38
+
39
+ JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003e', '<' => '\u003c', "\u2028" => '\u2028', "\u2029" => '\u2029' }
40
+ JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u
41
+
42
+ # Prior to version 4.1 of rails double quotes were inadventently removed in json_escape.
43
+ # This adds the correct json_escape functionality to rails versions < 4.1
44
+ def blazer_json_escape(s)
45
+ if Rails::VERSION::STRING < "4.1"
46
+ result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
47
+ s.html_safe? ? result.html_safe : result
48
+ else
49
+ json_escape(s)
50
+ end
51
+ end
52
+
53
+ def blazer_series_name(k)
54
+ k.nil? ? "null" : k.to_s
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,27 @@
1
+ module Blazer
2
+ class CheckMailer < ActionMailer::Base
3
+ include ActionView::Helpers::TextHelper
4
+
5
+ default from: Blazer.from_email if Blazer.from_email
6
+ layout false
7
+
8
+ def state_change(check, state, state_was, rows_count, error, columns, rows, column_types, check_type)
9
+ @check = check
10
+ @state = state
11
+ @state_was = state_was
12
+ @rows_count = rows_count
13
+ @error = error
14
+ @columns = columns
15
+ @rows = rows
16
+ @column_types = column_types
17
+ @check_type = check_type
18
+ mail to: check.emails, reply_to: check.emails, subject: "Check #{state.titleize}: #{check.query.name}"
19
+ end
20
+
21
+ def failing_checks(email, checks)
22
+ @checks = checks
23
+ # add reply_to for mailing lists
24
+ mail to: email, reply_to: email, subject: "#{pluralize(checks.size, "Check")} Failing"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,6 @@
1
+ module Blazer
2
+ class Audit < Record
3
+ belongs_to :user, Blazer::BELONGS_TO_OPTIONAL.merge(class_name: Blazer.user_class.to_s)
4
+ belongs_to :query, Blazer::BELONGS_TO_OPTIONAL
5
+ end
6
+ end
@@ -0,0 +1,95 @@
1
+ module Blazer
2
+ class Check < Record
3
+ belongs_to :creator, Blazer::BELONGS_TO_OPTIONAL.merge(class_name: Blazer.user_class.to_s) if Blazer.user_class
4
+ belongs_to :query
5
+
6
+ validates :query_id, presence: true
7
+ validate :validate_emails
8
+ validate :validate_variables, if: -> { query_id_changed? }
9
+
10
+ before_validation :set_state
11
+ before_validation :fix_emails
12
+
13
+ def split_emails
14
+ emails.to_s.downcase.split(",").map(&:strip)
15
+ end
16
+
17
+ def update_state(result)
18
+ check_type =
19
+ if respond_to?(:check_type)
20
+ self.check_type
21
+ elsif respond_to?(:invert)
22
+ invert ? "missing_data" : "bad_data"
23
+ else
24
+ "bad_data"
25
+ end
26
+
27
+ message = result.error
28
+
29
+ self.state =
30
+ if result.timed_out?
31
+ "timed out"
32
+ elsif result.error
33
+ "error"
34
+ elsif check_type == "anomaly"
35
+ anomaly, message = result.detect_anomaly
36
+ if anomaly.nil?
37
+ "error"
38
+ elsif anomaly
39
+ "failing"
40
+ else
41
+ "passing"
42
+ end
43
+ elsif result.rows.any?
44
+ check_type == "missing_data" ? "passing" : "failing"
45
+ else
46
+ check_type == "missing_data" ? "failing" : "passing"
47
+ end
48
+
49
+ self.last_run_at = Time.now if respond_to?(:last_run_at=)
50
+ self.message = message if respond_to?(:message=)
51
+
52
+ if respond_to?(:timeouts=)
53
+ if result.timed_out?
54
+ self.timeouts += 1
55
+ self.state = "disabled" if timeouts >= 3
56
+ else
57
+ self.timeouts = 0
58
+ end
59
+ end
60
+
61
+ # do not notify on creation, except when not passing
62
+ if (state_was != "new" || state != "passing") && state != state_was && emails.present?
63
+ Blazer::CheckMailer.state_change(self, state, state_was, result.rows.size, message, result.columns, result.rows.first(10).as_json, result.column_types, check_type).deliver_now
64
+ end
65
+ save! if changed?
66
+ end
67
+
68
+ private
69
+
70
+ def set_state
71
+ self.state ||= "new"
72
+ end
73
+
74
+ def fix_emails
75
+ # some people like doing ; instead of ,
76
+ # but we know what they mean, so let's fix it
77
+ # also, some people like to use whitespace
78
+ if emails.present?
79
+ self.emails = emails.strip.gsub(/[;\s]/, ",").gsub(/,+/, ", ")
80
+ end
81
+ end
82
+
83
+ def validate_emails
84
+ unless split_emails.all? { |e| e =~ /\A\S+@\S+\.\S+\z/ }
85
+ errors.add(:base, "Invalid emails")
86
+ end
87
+ end
88
+
89
+ def validate_variables
90
+ if query.variables.any?
91
+ errors.add(:base, "Query can't have variables")
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,5 @@
1
+ module Blazer
2
+ class Connection < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ module Blazer
2
+ class Dashboard < Record
3
+ belongs_to :creator, Blazer::BELONGS_TO_OPTIONAL.merge(class_name: Blazer.user_class.to_s) if Blazer.user_class
4
+ has_many :dashboard_queries, dependent: :destroy
5
+ has_many :queries, through: :dashboard_queries
6
+
7
+ validates :name, presence: true
8
+
9
+ def to_param
10
+ [id, name.gsub("'", "").parameterize].join("-")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Blazer
2
+ class DashboardQuery < Record
3
+ belongs_to :dashboard
4
+ belongs_to :query
5
+
6
+ validates :dashboard_id, presence: true
7
+ validates :query_id, presence: true
8
+ end
9
+ end
@@ -0,0 +1,31 @@
1
+ module Blazer
2
+ class Query < Record
3
+ belongs_to :creator, Blazer::BELONGS_TO_OPTIONAL.merge(class_name: Blazer.user_class.to_s) if Blazer.user_class
4
+ has_many :checks, dependent: :destroy
5
+ has_many :dashboard_queries, dependent: :destroy
6
+ has_many :dashboards, through: :dashboard_queries
7
+ has_many :audits
8
+
9
+ validates :statement, presence: true
10
+
11
+ scope :named, -> { where("blazer_queries.name <> ''") }
12
+
13
+ def to_param
14
+ [id, name].compact.join("-").gsub("'", "").parameterize
15
+ end
16
+
17
+ def friendly_name
18
+ name.to_s.sub(/\A[#\*]/, "").gsub(/\[.+\]/, "").strip
19
+ end
20
+
21
+ def editable?(user)
22
+ editable = !persisted? || (name.present? && name.first != "*" && name.first != "#") || user == try(:creator)
23
+ editable &&= Blazer.query_editable.call(self, user) if Blazer.query_editable
24
+ editable
25
+ end
26
+
27
+ def variables
28
+ Blazer.extract_vars(statement)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ module Blazer
2
+ class Record < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ <div class="btn-group" style="vertical-align: top; margin-right: 5px;">
2
+ <%= link_to "Home", root_path, class: "btn btn-primary" %>
3
+ <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
4
+ <span class="caret"></span>
5
+ <span class="sr-only">Toggle Dropdown</span>
6
+ </button>
7
+ <ul class="dropdown-menu">
8
+ <li><%= link_to "Dashboards", dashboards_path %></li>
9
+ <li><%= link_to "Checks", checks_path %></li>
10
+ <li role="separator" class="divider"></li>
11
+ <li><%= link_to "New Query", new_query_path %></li>
12
+ <li><%= link_to "New Dashboard", new_dashboard_path %></li>
13
+ <% check_params = @query ? {query_id: @query.id} : {} %>
14
+ <li><%= link_to "New Check", new_check_path(check_params) %></li>
15
+ </ul>
16
+ </div>
@@ -0,0 +1,102 @@
1
+ <% if @bind_vars.any? %>
2
+ <form id="bind" method="get" action="<%= action %>" class="form-inline" style="margin-bottom: 10px;">
3
+ <% date_vars = ["start_time", "end_time"] %>
4
+ <% if (date_vars - @bind_vars).empty? %>
5
+ <% @bind_vars = @bind_vars - date_vars %>
6
+ <% else %>
7
+ <% date_vars = nil %>
8
+ <% end %>
9
+
10
+ <% @bind_vars.each_with_index do |var, i| %>
11
+ <%= label_tag var, var %>
12
+ <% if (data = @smart_vars[var]) %>
13
+ <%= select_tag var, options_for_select([[nil, nil]] + data, selected: params[var]), style: "margin-right: 20px; width: 200px; display: none;" %>
14
+ <script>
15
+ $("#<%= var %>").selectize({
16
+ create: true
17
+ });
18
+ </script>
19
+ <% else %>
20
+ <%= text_field_tag var, params[var], style: "width: 120px; margin-right: 20px;", autofocus: i == 0 && !var.end_with?("_at") && !params[var], class: "form-control" %>
21
+ <% if var.end_with?("_at") %>
22
+ <script>
23
+ $("#<%= var %>").daterangepicker({singleDatePicker: true, locale: {format: "YYYY-MM-DD"}, autoUpdateInput: false});
24
+ // hack to start with empty date
25
+ $("#<%= var %>").on("apply.daterangepicker", function(ev, picker) {
26
+ $(this).val(picker.startDate.format("YYYY-MM-DD"));
27
+ $(this).change();
28
+ });
29
+ </script>
30
+ <% end %>
31
+ <% end %>
32
+ <% end %>
33
+
34
+ <% if date_vars %>
35
+ <% date_vars.each do |var| %>
36
+ <%= hidden_field_tag var, params[var] %>
37
+ <% end %>
38
+
39
+ <%= label_tag nil, date_vars.join(" & ") %>
40
+ <div class="selectize-control single" style="width: 300px;">
41
+ <div id="reportrange" class="selectize-input" style="display: inline-block;">
42
+ <span>Select a time range</span>
43
+ </div>
44
+ </div>
45
+
46
+ <script>
47
+ <%= blazer_js_var "timeZone", Blazer.time_zone.tzinfo.name %>
48
+ var format = "YYYY-MM-DD"
49
+ var now = moment.tz(timeZone)
50
+
51
+ function dateStr(daysAgo) {
52
+ return now.clone().subtract(daysAgo || 0, "days").format(format)
53
+ }
54
+
55
+ function toDate(time) {
56
+ return moment.tz(time.format(format), timeZone)
57
+ }
58
+
59
+ function setTimeInputs(start, end) {
60
+ $("#start_time").val(toDate(start).utc().format())
61
+ $("#end_time").val(toDate(end).endOf("day").utc().format())
62
+ }
63
+
64
+ $("#reportrange").daterangepicker(
65
+ {
66
+ ranges: {
67
+ "Today": [dateStr(), dateStr()],
68
+ "Last 7 Days": [dateStr(6), dateStr()],
69
+ "Last 30 Days": [dateStr(29), dateStr()]
70
+ },
71
+ locale: {
72
+ format: format
73
+ },
74
+ startDate: dateStr(29),
75
+ endDate: dateStr(),
76
+ opens: "right"
77
+ },
78
+ function(start, end) {
79
+ setTimeInputs(start, end)
80
+ submitIfCompleted($("#start_time").closest("form"))
81
+ }
82
+ ).on("apply.daterangepicker", function(ev, picker) {
83
+ setTimeInputs(picker.startDate, picker.endDate)
84
+ $("#reportrange span").html(toDate(picker.startDate).format("MMMM D, YYYY") + " - " + toDate(picker.endDate).format("MMMM D, YYYY"))
85
+ })
86
+
87
+ if ($("#start_time").val().length > 0) {
88
+ var picker = $("#reportrange").data("daterangepicker")
89
+ picker.setStartDate(moment.tz($("#start_time").val(), timeZone))
90
+ picker.setEndDate(moment.tz($("#end_time").val(), timeZone))
91
+ $("#reportrange").trigger("apply.daterangepicker", picker)
92
+ } else {
93
+ var picker = $("#reportrange").data("daterangepicker")
94
+ $("#reportrange").trigger("apply.daterangepicker", picker)
95
+ submitIfCompleted($("#start_time").closest("form"))
96
+ }
97
+ </script>
98
+ <% end %>
99
+
100
+ <input type="submit" class="btn btn-success" value="Run" style="vertical-align: top;" />
101
+ </form>
102
+ <% end %>
@@ -0,0 +1,6 @@
1
+ <ul>
2
+ <% @checks.each do |check| %>
3
+ <li><%= link_to check.query.name, query_url(check.query_id) %> <%= check.state %></li>
4
+ <% end %>
5
+ </ul>
6
+ <p><%= link_to "Manage checks", checks_url %></p>
@@ -0,0 +1,47 @@
1
+ <html>
2
+ <head>
3
+ </head>
4
+ <body style="font-family: 'Helvetica Neue', Arial, Helvetica; font-size: 14px; color: #333;">
5
+ <p><%= link_to "View", query_url(@check.query_id) %></p>
6
+ <% if @error %>
7
+ <p><%= @error %></p>
8
+ <% elsif @rows_count > 0 && @check_type == "bad_data" %>
9
+ <p>
10
+ <% if @rows_count <= 10 %>
11
+ <%= pluralize(@rows_count, "row") %>
12
+ <% else %>
13
+ Showing 10 of <%= @rows_count %> rows
14
+ <% end %>
15
+ </p>
16
+ <table style="width: 100%; border-spacing: 0; border-collapse: collapse;">
17
+ <thead>
18
+ <tr>
19
+ <% @columns.first(5).each do |column| %>
20
+ <th style="padding: 8px; line-height: 1.4; text-align: left; vertical-align: bottom; border-bottom: 2px solid #ddd; width: <%= (100 / @columns.size).round(2) %>%;">
21
+ <%= column %>
22
+ </th>
23
+ <% end %>
24
+ </tr>
25
+ </thead>
26
+ <tbody>
27
+ <% @rows.first(10).each do |row| %>
28
+ <tr>
29
+ <% @columns.first(5).each_with_index do |column, i| %>
30
+ <td style="padding: 8px; line-height: 1.4; vertical-align: top; border-top: 1px solid #ddd;">
31
+ <% value = row[i] %>
32
+ <% if @column_types[i] == "time" && value.to_s.length > 10 %>
33
+ <% value = Time.parse(value).in_time_zone(Blazer.time_zone) rescue value %>
34
+ <% end %>
35
+ <%= value %>
36
+ </td>
37
+ <% end %>
38
+ </tr>
39
+ <% end %>
40
+ </tbody>
41
+ </table>
42
+ <% if @columns.size > 5 %>
43
+ <p style="color: #999;">Only first 5 columns shown</p>
44
+ <% end %>
45
+ <% end %>
46
+ </body>
47
+ </html>
@@ -0,0 +1,71 @@
1
+ <% unless @check.respond_to?(:invert) %>
2
+ <p class="text-muted">Checks are designed to identify bad data. A check fails if there are any results.</p>
3
+ <% end %>
4
+
5
+ <% if @check.errors.any? %>
6
+ <div class="alert alert-danger"><%= @check.errors.full_messages.first %></div>
7
+ <% end %>
8
+
9
+ <%= form_for @check do |f| %>
10
+ <div class="form-group">
11
+ <%= f.label :query_id, "Query" %>
12
+ <div class="hide">
13
+ <%= f.select :query_id, [], {include_blank: true} %>
14
+ </div>
15
+ <script>
16
+ <%= blazer_js_var "queries", Blazer::Query.named.order(:name).select("id, name").map { |q| {text: q.name, value: q.id} } %>
17
+ <%= blazer_js_var "items", [@check.query_id].compact %>
18
+
19
+ $("#check_query_id").selectize({options: queries, items: items, highlight: false, maxOptions: 100}).parents(".hide").removeClass("hide");
20
+ </script>
21
+ </div>
22
+
23
+ <% if @check.respond_to?(:check_type) %>
24
+ <div class="form-group">
25
+ <%= f.label :check_type, "Alert if" %>
26
+ <div class="hide">
27
+ <% check_options = [["Any results (bad data)", "bad_data"], ["No results (missing data)", "missing_data"]] %>
28
+ <% check_options << ["Anomaly (most recent data point)", "anomaly"] if Blazer.anomaly_checks %>
29
+ <%= f.select :check_type, check_options %>
30
+ </div>
31
+ <script>
32
+ $("#check_check_type").selectize({}).parent().removeClass("hide");
33
+ </script>
34
+ </div>
35
+ <% elsif @check.respond_to?(:invert) %>
36
+ <div class="form-group">
37
+ <%= f.label :invert, "Fails if" %>
38
+ <div class="hide">
39
+ <%= f.select :invert, [["Any results (bad data)", false], ["No results (missing data)", true]] %>
40
+ </div>
41
+ <script>
42
+ $("#check_invert").selectize({}).parent().removeClass("hide");
43
+ </script>
44
+ </div>
45
+ <% end %>
46
+
47
+ <% if @check.respond_to?(:schedule) && Blazer.check_schedules %>
48
+ <div class="form-group">
49
+ <%= f.label :schedule, "Run every" %>
50
+ <div class="hide">
51
+ <%= f.select :schedule, Blazer.check_schedules.map { |v| [v, v] } %>
52
+ </div>
53
+ <script>
54
+ $("#check_schedule").selectize({}).parent().removeClass("hide");
55
+ </script>
56
+ </div>
57
+ <% end %>
58
+
59
+ <div class="form-group">
60
+ <%= f.label :emails %>
61
+ <%= f.text_field :emails, placeholder: "Optional, comma separated", class: "form-control" %>
62
+ </div>
63
+ <p class="text-muted">Emails are sent when a check starts failing, and when it starts passing again.
64
+ <p>
65
+ <% if @check.persisted? %>
66
+ <%= link_to "Delete", check_path(@check), method: :delete, "data-confirm" => "Are you sure?", class: "btn btn-danger" %>
67
+ <% end %>
68
+ <%= f.submit "Save", class: "btn btn-success" %>
69
+ <%= link_to "Back", :back, class: "btn btn-link" %>
70
+ </p>
71
+ <% end %>