sql-jarvis 2.0.1 → 2.0.2

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -1
  3. data/LICENSE.txt +1 -1
  4. data/README.md +153 -52
  5. data/app/assets/fonts/blazer/glyphicons-halflings-regular.eot +0 -0
  6. data/app/assets/fonts/blazer/glyphicons-halflings-regular.svg +0 -0
  7. data/app/assets/fonts/blazer/glyphicons-halflings-regular.ttf +0 -0
  8. data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff +0 -0
  9. data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff2 +0 -0
  10. data/app/assets/images/blazer/favicon.png +0 -0
  11. data/app/assets/javascripts/blazer/Chart.js +4195 -3884
  12. data/app/assets/javascripts/blazer/Sortable.js +1493 -1097
  13. data/app/assets/javascripts/blazer/ace/ace.js +21294 -4
  14. data/app/assets/javascripts/blazer/ace/ext-language_tools.js +1991 -3
  15. data/app/assets/javascripts/blazer/ace/mode-sql.js +110 -1
  16. data/app/assets/javascripts/blazer/ace/snippets/sql.js +40 -1
  17. data/app/assets/javascripts/blazer/ace/snippets/text.js +14 -1
  18. data/app/assets/javascripts/blazer/ace/theme-twilight.js +116 -1
  19. data/app/assets/javascripts/blazer/application.js +4 -3
  20. data/app/assets/javascripts/blazer/bootstrap.js +623 -612
  21. data/app/assets/javascripts/blazer/chartkick.js +1769 -1248
  22. data/app/assets/javascripts/blazer/daterangepicker.js +263 -115
  23. data/app/assets/javascripts/blazer/highlight.min.js +3 -0
  24. data/app/assets/javascripts/blazer/{jquery_ujs.js → jquery-ujs.js} +161 -75
  25. data/app/assets/javascripts/blazer/jquery.js +9506 -9450
  26. data/app/assets/javascripts/blazer/jquery.stickytableheaders.js +321 -259
  27. data/app/assets/javascripts/blazer/moment-timezone-with-data.js +1212 -0
  28. data/app/assets/javascripts/blazer/queries.js +1 -1
  29. data/app/assets/javascripts/blazer/routes.js +3 -0
  30. data/app/assets/javascripts/blazer/selectize.js +3828 -3604
  31. data/app/assets/javascripts/blazer/stupidtable.js +255 -88
  32. data/app/assets/javascripts/blazer/vue.js +8015 -4583
  33. data/app/assets/stylesheets/blazer/application.css +41 -5
  34. data/app/assets/stylesheets/blazer/bootstrap.css.erb +879 -325
  35. data/app/assets/stylesheets/blazer/daterangepicker.css +269 -0
  36. data/app/assets/stylesheets/blazer/selectize.default.css +26 -10
  37. data/app/controllers/blazer/base_controller.rb +7 -0
  38. data/app/controllers/blazer/checks_controller.rb +1 -1
  39. data/app/controllers/blazer/dashboards_controller.rb +0 -4
  40. data/app/controllers/blazer/queries_controller.rb +20 -12
  41. data/app/helpers/blazer/base_helper.rb +1 -1
  42. data/app/mailers/blazer/slack_notifier.rb +76 -0
  43. data/app/models/blazer/check.rb +9 -0
  44. data/app/views/blazer/_nav.html.erb +0 -1
  45. data/app/views/blazer/_variables.html.erb +41 -19
  46. data/app/views/blazer/checks/_form.html.erb +16 -8
  47. data/app/views/blazer/checks/edit.html.erb +2 -0
  48. data/app/views/blazer/checks/index.html.erb +33 -4
  49. data/app/views/blazer/checks/new.html.erb +2 -0
  50. data/app/views/blazer/dashboards/_form.html.erb +4 -4
  51. data/app/views/blazer/dashboards/edit.html.erb +2 -0
  52. data/app/views/blazer/dashboards/new.html.erb +2 -0
  53. data/app/views/blazer/dashboards/show.html.erb +7 -3
  54. data/app/views/blazer/queries/_form.html.erb +11 -6
  55. data/app/views/blazer/queries/docs.html.erb +131 -0
  56. data/app/views/blazer/queries/home.html.erb +12 -3
  57. data/app/views/blazer/queries/run.html.erb +36 -6
  58. data/app/views/blazer/queries/schema.html.erb +46 -8
  59. data/app/views/blazer/queries/show.html.erb +4 -4
  60. data/app/views/layouts/blazer/application.html.erb +3 -3
  61. data/config/routes.rb +5 -1
  62. data/lib/blazer.rb +32 -13
  63. data/lib/blazer/adapters/athena_adapter.rb +1 -1
  64. data/lib/blazer/adapters/elasticsearch_adapter.rb +14 -17
  65. data/lib/blazer/adapters/mongodb_adapter.rb +1 -1
  66. data/lib/blazer/adapters/sql_adapter.rb +7 -1
  67. data/lib/blazer/engine.rb +4 -0
  68. data/lib/blazer/result.rb +62 -29
  69. data/lib/blazer/run_statement_job.rb +6 -9
  70. data/lib/blazer/version.rb +1 -1
  71. data/lib/generators/blazer/templates/config.yml.tt +13 -2
  72. data/lib/generators/blazer/templates/install.rb.tt +1 -0
  73. data/lib/tasks/blazer.rake +1 -0
  74. metadata +33 -37
  75. data/.gitattributes +0 -1
  76. data/.github/ISSUE_TEMPLATE.md +0 -7
  77. data/.gitignore +0 -14
  78. data/Gemfile +0 -7
  79. data/Rakefile +0 -1
  80. data/app/assets/javascripts/blazer/highlight.pack.js +0 -1
  81. data/app/assets/javascripts/blazer/moment-timezone.js +0 -1007
  82. data/app/assets/stylesheets/blazer/daterangepicker-bs3.css +0 -375
  83. data/blazer.gemspec +0 -30
@@ -12,7 +12,7 @@ module Blazer
12
12
  BLAZER_IMAGE_EXT = %w[png jpg jpeg gif]
13
13
 
14
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")
15
+ if value.is_a?(Numeric) && !key.to_s.end_with?("id") && !key.to_s.start_with?("id")
16
16
  number_with_delimiter(value)
17
17
  elsif value =~ BLAZER_URL_REGEX
18
18
  # see if image or link
@@ -0,0 +1,76 @@
1
+ require "net/http"
2
+
3
+ module Blazer
4
+ class SlackNotifier
5
+ def self.state_change(check, state, state_was, rows_count, error, check_type)
6
+ check.split_slack_channels.each do |channel|
7
+ text =
8
+ if error
9
+ error
10
+ elsif rows_count > 0 && check_type == "bad_data"
11
+ pluralize(rows_count, "row")
12
+ end
13
+
14
+ payload = {
15
+ channel: channel,
16
+ attachments: [
17
+ {
18
+ title: escape("Check #{state.titleize}: #{check.query.name}"),
19
+ title_link: query_url(check.query_id),
20
+ text: escape(text),
21
+ color: state == "passing" ? "good" : "danger"
22
+ }
23
+ ]
24
+ }
25
+
26
+ post(Blazer.slack_webhook_url, payload)
27
+ end
28
+ end
29
+
30
+ def self.failing_checks(channel, checks)
31
+ text =
32
+ checks.map do |check|
33
+ "<#{query_url(check.query_id)}|#{escape(check.query.name)}> #{escape(check.state)}"
34
+ end
35
+
36
+ payload = {
37
+ channel: channel,
38
+ attachments: [
39
+ {
40
+ title: escape("#{pluralize(checks.size, "Check")} Failing"),
41
+ text: text.join("\n"),
42
+ color: "warning"
43
+ }
44
+ ]
45
+ }
46
+
47
+ post(Blazer.slack_webhook_url, payload)
48
+ end
49
+
50
+ # https://api.slack.com/docs/message-formatting#how_to_escape_characters
51
+ # - Replace the ampersand, &, with &amp;
52
+ # - Replace the less-than sign, < with &lt;
53
+ # - Replace the greater-than sign, > with &gt;
54
+ # That's it. Don't HTML entity-encode the entire message.
55
+ def self.escape(str)
56
+ str.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;") if str
57
+ end
58
+
59
+ def self.pluralize(*args)
60
+ ActionController::Base.helpers.pluralize(*args)
61
+ end
62
+
63
+ def self.query_url(id)
64
+ Blazer::Engine.routes.url_helpers.query_url(id, ActionMailer::Base.default_url_options)
65
+ end
66
+
67
+ def self.post(url, payload)
68
+ uri = URI.parse(url)
69
+ http = Net::HTTP.new(uri.host, uri.port)
70
+ http.use_ssl = true
71
+ http.open_timeout = 3
72
+ http.read_timeout = 5
73
+ http.post(uri.request_uri, payload.to_json)
74
+ end
75
+ end
76
+ end
@@ -14,6 +14,14 @@ module Blazer
14
14
  emails.to_s.downcase.split(",").map(&:strip)
15
15
  end
16
16
 
17
+ def split_slack_channels
18
+ if Blazer.slack?
19
+ slack_channels.to_s.downcase.split(",").map(&:strip)
20
+ else
21
+ []
22
+ end
23
+ end
24
+
17
25
  def update_state(result)
18
26
  check_type =
19
27
  if respond_to?(:check_type)
@@ -61,6 +69,7 @@ module Blazer
61
69
  # do not notify on creation, except when not passing
62
70
  if (state_was != "new" || state != "passing") && state != state_was && emails.present?
63
71
  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
72
+ Blazer::SlackNotifier.state_change(self, state, state_was, result.rows.size, message, check_type)
64
73
  end
65
74
  save! if changed?
66
75
  end
@@ -5,7 +5,6 @@
5
5
  <span class="sr-only">Toggle Dropdown</span>
6
6
  </button>
7
7
  <ul class="dropdown-menu">
8
- <li><%= link_to "Dashboards", dashboards_path %></li>
9
8
  <li><%= link_to "Checks", checks_path %></li>
10
9
  <li role="separator" class="divider"></li>
11
10
  <li><%= link_to "New Query", new_query_path %></li>
@@ -1,5 +1,14 @@
1
1
  <% if @bind_vars.any? %>
2
- <form id="bind" method="get" action="<%= action %>" class="form-inline" style="margin-bottom: 10px;">
2
+ <script>
3
+ <%= blazer_js_var "timeZone", Blazer.time_zone.tzinfo.name %>
4
+ var now = moment.tz(timeZone)
5
+ var format = "YYYY-MM-DD"
6
+
7
+ function toDate(time) {
8
+ return moment.tz(time.format(format), timeZone)
9
+ }
10
+ </script>
11
+ <form id="bind" method="get" action="<%= action %>" class="form-inline" style="margin-bottom: 15px;">
3
12
  <% date_vars = ["start_time", "end_time"] %>
4
13
  <% if (date_vars - @bind_vars).empty? %>
5
14
  <% @bind_vars = @bind_vars - date_vars %>
@@ -16,18 +25,39 @@
16
25
  create: true
17
26
  });
18
27
  </script>
28
+ <% elsif var.end_with?("_at") || var == "start_time" || var == "end_time" %>
29
+ <%= hidden_field_tag var, params[var] %>
30
+
31
+ <div class="selectize-control single" style="width: 200px;">
32
+ <div id="<%= var %>-select" class="selectize-input" style="display: inline-block;">
33
+ <span>Select a date</span>
34
+ </div>
35
+ </div>
36
+
37
+ <script>
38
+ (function() {
39
+ var input = $("#<%= var %>")
40
+ var datePicker = $("#<%= var %>-select")
41
+ datePicker.daterangepicker({
42
+ singleDatePicker: true,
43
+ locale: {format: format},
44
+ autoUpdateInput: false,
45
+ startDate: input.val().length > 0 ? moment.tz(input.val(), timeZone) : now
46
+ })
47
+ // hack to start with empty date
48
+ datePicker.on("apply.daterangepicker", function(ev, picker) {
49
+ datePicker.find("span").html(toDate(picker.startDate).format("MMMM D, YYYY"))
50
+ input.val(toDate(picker.startDate).utc().format())
51
+ submitIfCompleted($("#<%= var %>").closest("form"))
52
+ })
53
+ if (input.val().length > 0) {
54
+ var picker = datePicker.data("daterangepicker")
55
+ datePicker.find("span").html(toDate(picker.startDate).format("MMMM D, YYYY"))
56
+ }
57
+ })()
58
+ </script>
19
59
  <% else %>
20
60
  <%= 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
61
  <% end %>
32
62
  <% end %>
33
63
 
@@ -44,18 +74,10 @@
44
74
  </div>
45
75
 
46
76
  <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
77
  function dateStr(daysAgo) {
52
78
  return now.clone().subtract(daysAgo || 0, "days").format(format)
53
79
  }
54
80
 
55
- function toDate(time) {
56
- return moment.tz(time.format(format), timeZone)
57
- }
58
-
59
81
  function setTimeInputs(start, end) {
60
82
  $("#start_time").val(toDate(start).utc().format())
61
83
  $("#end_time").val(toDate(end).endOf("day").utc().format())
@@ -1,12 +1,12 @@
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 %>
1
+ <%= form_for @check, html: {class: "small-form"} do |f| %>
2
+ <% unless @check.respond_to?(:check_type) || @check.respond_to?(:invert) %>
3
+ <p class="text-muted">Checks are designed to identify bad data. A check fails if there are any results.</p>
4
+ <% end %>
4
5
 
5
- <% if @check.errors.any? %>
6
- <div class="alert alert-danger"><%= @check.errors.full_messages.first %></div>
7
- <% end %>
6
+ <% if @check.errors.any? %>
7
+ <div class="alert alert-danger"><%= @check.errors.full_messages.first %></div>
8
+ <% end %>
8
9
 
9
- <%= form_for @check do |f| %>
10
10
  <div class="form-group">
11
11
  <%= f.label :query_id, "Query" %>
12
12
  <div class="hide">
@@ -60,7 +60,15 @@
60
60
  <%= f.label :emails %>
61
61
  <%= f.text_field :emails, placeholder: "Optional, comma separated", class: "form-control" %>
62
62
  </div>
63
- <p class="text-muted">Emails are sent when a check starts failing, and when it starts passing again.
63
+
64
+ <% if Blazer.slack? %>
65
+ <div class="form-group">
66
+ <%= f.label :slack_channels %>
67
+ <%= f.text_field :slack_channels, placeholder: "Optional, comma separated", class: "form-control" %>
68
+ </div>
69
+ <% end %>
70
+
71
+ <p class="text-muted">Emails <%= Blazer.slack? ? "and Slack notifications " : nil %>are sent when a check starts failing, and when it starts passing again.
64
72
  <p>
65
73
  <% if @check.persisted? %>
66
74
  <%= link_to "Delete", check_path(@check), method: :delete, "data-confirm" => "Are you sure?", class: "btn btn-danger" %>
@@ -1 +1,3 @@
1
+ <% blazer_title "Edit Check" %>
2
+
1
3
  <%= render partial: "form" %>
@@ -1,15 +1,32 @@
1
1
  <% blazer_title "Checks" %>
2
2
 
3
- <p style="float: right;"><%= link_to "New Check", new_check_path, class: "btn btn-info" %></p>
4
- <%= render partial: "blazer/nav" %>
3
+ <div id="header">
4
+ <div class="pull-right" style="line-height: 34px;">
5
+ <div class="btn-group">
6
+ <%= link_to "New Check", new_check_path, class: "btn btn-info" %>
7
+ <button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
8
+ <span class="caret"></span>
9
+ <span class="sr-only">Toggle Dropdown</span>
10
+ </button>
11
+ <ul class="dropdown-menu">
12
+ <li><%= link_to "Home", root_path %></li>
13
+ <li role="separator" class="divider"></li>
14
+ <li><%= link_to "New Query", new_query_path %></li>
15
+ <li><%= link_to "New Dashboard", new_dashboard_path %></li>
16
+ </ul>
17
+ </div>
18
+ </div>
5
19
 
6
- <table class="table">
20
+ <input id="search" type="text" placeholder="Start typing a query or state" style="width: 300px; display: inline-block;" class="search form-control" />
21
+ </div>
22
+
23
+ <table id="checks" class="table">
7
24
  <thead>
8
25
  <tr>
9
26
  <th>Query</th>
10
27
  <th style="width: 10%;">State</th>
11
28
  <th style="width: 10%;">Run</th>
12
- <th style="width: 20%;">Emails</th>
29
+ <th style="width: 20%;">Notify</th>
13
30
  <th style="width: 15%;"></th>
14
31
  </tr>
15
32
  </thead>
@@ -28,6 +45,9 @@
28
45
  <% check.split_emails.each do |email| %>
29
46
  <li><%= email %></li>
30
47
  <% end %>
48
+ <% check.split_slack_channels.each do |channel| %>
49
+ <li><%= channel %></li>
50
+ <% end %>
31
51
  </ul>
32
52
  </td>
33
53
  <td style="text-align: right; padding: 1px;">
@@ -38,3 +58,12 @@
38
58
  <% end %>
39
59
  </tbody>
40
60
  </table>
61
+
62
+ <script>
63
+ $("#search").on("keyup", function() {
64
+ var value = $(this).val().toLowerCase()
65
+ $("#checks tbody tr").filter( function() {
66
+ $(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
67
+ })
68
+ }).focus()
69
+ </script>
@@ -1 +1,3 @@
1
+ <% blazer_title "New Check" %>
2
+
1
3
  <%= render partial: "form" %>
@@ -1,8 +1,8 @@
1
- <% if @dashboard.errors.any? %>
2
- <div class="alert alert-danger"><%= @dashboard.errors.full_messages.first %></div>
3
- <% end %>
1
+ <%= form_for @dashboard, url: (@dashboard.persisted? ? dashboard_path(@dashboard, variable_params) : dashboards_path(variable_params)), html: {id: "app", class: "small-form"} do |f| %>
2
+ <% if @dashboard.errors.any? %>
3
+ <div class="alert alert-danger"><%= @dashboard.errors.full_messages.first %></div>
4
+ <% end %>
4
5
 
5
- <%= form_for @dashboard, url: (@dashboard.persisted? ? dashboard_path(@dashboard, variable_params) : dashboards_path(variable_params)), html: {id: "app"} do |f| %>
6
6
  <div class="form-group">
7
7
  <%= f.label :name %>
8
8
  <%= f.text_field :name, class: "form-control" %>
@@ -1 +1,3 @@
1
+ <% blazer_title "Edit Dashboard" %>
2
+
1
3
  <%= render partial: "form" %>
@@ -1 +1,3 @@
1
+ <% blazer_title "New Dashboard" %>
2
+
1
3
  <%= render partial: "form" %>
@@ -5,7 +5,7 @@
5
5
  <div class="row" style="padding-top: 13px;">
6
6
  <div class="col-sm-9">
7
7
  <%= render partial: "blazer/nav" %>
8
- <h3 style="margin: 0; line-height: 34px; display: inline;">
8
+ <h3 style="line-height: 34px; display: inline; margin-left: 5px;">
9
9
  <%= @dashboard.name %>
10
10
  </h3>
11
11
  </div>
@@ -25,7 +25,11 @@
25
25
  </p>
26
26
  <% end %>
27
27
 
28
- <%= render partial: "blazer/variables", locals: {action: dashboard_path(@dashboard)} %>
28
+ <% if @bind_vars.any? %>
29
+ <%= render partial: "blazer/variables", locals: {action: dashboard_path(@dashboard)} %>
30
+ <% else %>
31
+ <div style="padding-bottom: 15px;"></div>
32
+ <% end %>
29
33
 
30
34
  <% @queries.each_with_index do |query, i| %>
31
35
  <div class="chart-container">
@@ -35,7 +39,7 @@
35
39
  </div>
36
40
  </div>
37
41
  <script>
38
- <%= blazer_js_var "data", {statement: query.statement, query_id: query.id, only_chart: true} %>
42
+ <%= blazer_js_var "data", {statement: query.statement, query_id: query.id, data_source: query.data_source, only_chart: true} %>
39
43
 
40
44
  runQuery(data, function (data) {
41
45
  $("#chart-<%= i %>").html(data)
@@ -12,11 +12,13 @@
12
12
  <div id="editor" :style="{ height: editorHeight }"><%= @query.statement %></div>
13
13
  </div>
14
14
  </div>
15
- <div class="form-group text-right">
16
- <div class="pull-left" style="margin-top: 9px;">
15
+ <div class="form-group text-right" style="margin-bottom: 8px;">
16
+ <div class="pull-left" style="margin-top: 8px;">
17
17
  <%= link_to "Back", :back %>
18
+ <a :href="docsPath" target="_blank" style="margin-left: 40px;">Docs</a>
19
+ <a :href="schemaPath" target="_blank" style="margin-left: 40px;">Schema</a>
18
20
  </div>
19
- <a :href="dataSourcePath" target="_blank" style="margin-right: 10px;">Schema</a>
21
+
20
22
  <%= f.select :data_source, Blazer.data_sources.values.select { |ds| q = @query.dup; q.data_source = ds.id; q.editable?(blazer_user) }.map { |ds| [ds.name, ds.id] }, {}, class: ("hide" if Blazer.data_sources.size <= 1), style: "width: 140px;" %>
21
23
  <div id="tables" style="display: inline-block; width: 250px; margin-right: 10px;">
22
24
  <select id="table_names" style="width: 240px;" placeholder="Preview table"></select>
@@ -40,7 +42,7 @@
40
42
  <%= f.collection_select :assignee_ids, @assignees, :first, :last, {}, { placeholder: "Assignees", style: "height: 80px;", class: "form-control", multiple: true } %>
41
43
  </div>
42
44
  <%- end -%>
43
- <div class="text-right">
45
+ <div class="form-group text-right">
44
46
  <%= f.submit "For Enter Press", class: "hide" %>
45
47
  <% if @query.persisted? %>
46
48
  <%= link_to "Delete", query_path(@query), method: :delete, "data-confirm" => "Are you sure?", class: "btn btn-danger" %>
@@ -55,7 +57,7 @@
55
57
  <% words << pluralize(dashboards_count, "dashboard") if dashboards_count > 0 %>
56
58
  <% words << pluralize(checks_count, "check") if checks_count > 0 %>
57
59
  <% if words.any? %>
58
- <div class="alert alert-info" style="margin-top: 10px; padding: 8px 12px;">
60
+ <div class="alert alert-info" style="margin-bottom: 0;">
59
61
  Part of <%= words.to_sentence %>. Be careful when editing.
60
62
  </div>
61
63
  <% end %>
@@ -85,8 +87,11 @@
85
87
  editorHeight: "180px"
86
88
  },
87
89
  computed: {
88
- dataSourcePath: function() {
90
+ schemaPath: function() {
89
91
  return Routes.schema_queries_path({data_source: this.dataSource})
92
+ },
93
+ docsPath: function() {
94
+ return Routes.docs_queries_path({data_source: this.dataSource})
90
95
  }
91
96
  },
92
97
  methods: {
@@ -0,0 +1,131 @@
1
+ <% blazer_title "Docs: #{@data_source.name}" %>
2
+
3
+ <h1>Docs: <%= @data_source.name %></h1>
4
+
5
+ <hr />
6
+
7
+ <h2>Smart Variables</h2>
8
+
9
+ <% if @smart_variables.any? %>
10
+ <p>Use these variable names to get a dropdown of values.</p>
11
+
12
+ <table class="table" style="max-width: 500px;">
13
+ <thead>
14
+ <tr>
15
+ <th>Variable</th>
16
+ </tr>
17
+ </thead>
18
+ <tbody>
19
+ <% @smart_variables.each do |k, _| %>
20
+ <tr>
21
+ <td><code>{<%= k %>}</code></td>
22
+ </tr>
23
+ <% end %>
24
+ </tbody>
25
+ </table>
26
+
27
+ <p>Use <code>{start_time}</code> and <code>{end_time}</code> for a date range selector. End a variable name with <code>_at</code> for a date selector.</p>
28
+ <% else %>
29
+ <p>None set - add them in <code>config/blazer.yml</code>.</p>
30
+ <% end %>
31
+
32
+ <h2>Linked Columns</h2>
33
+
34
+ <% if @linked_columns.any? %>
35
+ <p>Use these column names to link results to other pages.</p>
36
+
37
+ <table class="table" style="max-width: 500px;">
38
+ <thead>
39
+ <tr>
40
+ <th style="width: 20%;">Name</th>
41
+ <th>URL</th>
42
+ </tr>
43
+ </thead>
44
+ <tbody>
45
+ <% @linked_columns.each do |k, v| %>
46
+ <tr>
47
+ <td><%= k %></td>
48
+ <td><%= v %></td>
49
+ </tr>
50
+ <% end %>
51
+ </tbody>
52
+ </table>
53
+
54
+ <p>Values that match the format of a URL will be linked automatically.</p>
55
+ <% else %>
56
+ <p>None set - add them in <code>config/blazer.yml</code>.</p>
57
+ <% end %>
58
+
59
+ <h2>Smart Columns</h2>
60
+
61
+ <% if @smart_columns.any? %>
62
+ <p>Use these column names to show additional data.</p>
63
+
64
+ <table class="table" style="max-width: 500px;">
65
+ <thead>
66
+ <tr>
67
+ <th>Name</th>
68
+ </tr>
69
+ </thead>
70
+ <tbody>
71
+ <% @smart_columns.each do |k, _| %>
72
+ <tr>
73
+ <td><%= k %></td>
74
+ </tr>
75
+ <% end %>
76
+ </tbody>
77
+ </table>
78
+ <% else %>
79
+ <p>None set - add them in <code>config/blazer.yml</code>.</p>
80
+ <% end %>
81
+
82
+ <h2>Charts</h2>
83
+
84
+ <p>Use specific combinations of column types to generate charts.</p>
85
+
86
+ <table class="table" style="max-width: 500px;">
87
+ <thead>
88
+ <tr>
89
+ <th style="width: 20%;">Chart</th>
90
+ <th>Column Types</th>
91
+ </tr>
92
+ </thead>
93
+ <tbody>
94
+ <tr>
95
+ <td>Line</td>
96
+ <td>2+ columns - timestamp, numeric(s)</td>
97
+ </tr>
98
+ <tr>
99
+ <td>Line</td>
100
+ <td>3 columns - timestamp, string, numeric</td>
101
+ </tr>
102
+ <tr>
103
+ <td>Column</td>
104
+ <td>2+ columns - string, numeric(s)</td>
105
+ </tr>
106
+ <tr>
107
+ <td>Column</td>
108
+ <td>3 columns - string, string, numeric</td>
109
+ </tr>
110
+ <tr>
111
+ <td>Scatter</td>
112
+ <td>2 columns - both numeric</td>
113
+ </tr>
114
+ <tr>
115
+ <td>Pie</td>
116
+ <td>2 columns - string, numeric - and last column named <code>pie</code></td>
117
+ </tr>
118
+ <tr>
119
+ <td>Map</td>
120
+ <td>
121
+ Named <code>latitude</code> and <code>longitude</code>, or <code>lat</code> and <code>lon</code>, or <code>lat</code> and <code>lng</code>
122
+ <% if !blazer_maps? %>
123
+ <br />
124
+ <strong>Needs configured</strong>
125
+ <% end %>
126
+ </td>
127
+ </tr>
128
+ </tbody>
129
+ </table>
130
+
131
+ <p>Use the column name <code>target</code> to draw a line for goals.</p>