blazer 2.2.5 → 2.3.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of blazer might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/LICENSE.txt +1 -1
- data/README.md +113 -29
- data/app/assets/javascripts/blazer/Chart.js +13794 -12099
- data/app/assets/javascripts/blazer/Sortable.js +3695 -1526
- data/app/assets/javascripts/blazer/chartkick.js +296 -46
- data/app/assets/javascripts/blazer/daterangepicker.js +194 -269
- data/app/assets/javascripts/blazer/jquery.js +1150 -642
- data/app/assets/javascripts/blazer/moment-timezone-with-data.js +621 -287
- data/app/assets/javascripts/blazer/moment.js +5085 -2460
- data/app/assets/stylesheets/blazer/daterangepicker.css +394 -253
- data/app/controllers/blazer/base_controller.rb +4 -4
- data/app/controllers/blazer/dashboards_controller.rb +5 -2
- data/app/controllers/blazer/queries_controller.rb +11 -2
- data/app/controllers/blazer/uploads_controller.rb +147 -0
- data/app/mailers/blazer/slack_notifier.rb +1 -1
- data/app/models/blazer/query.rb +1 -0
- data/app/models/blazer/upload.rb +11 -0
- data/app/models/blazer/uploads_connection.rb +7 -0
- data/app/views/blazer/_nav.html.erb +3 -0
- data/app/views/blazer/_variables.html.erb +3 -1
- data/app/views/blazer/checks/_form.html.erb +1 -1
- data/app/views/blazer/checks/index.html.erb +3 -0
- data/app/views/blazer/dashboards/_form.html.erb +1 -1
- data/app/views/blazer/dashboards/show.html.erb +1 -1
- data/app/views/blazer/queries/home.html.erb +3 -0
- data/app/views/blazer/queries/run.html.erb +5 -5
- data/app/views/blazer/queries/show.html.erb +3 -1
- data/app/views/blazer/uploads/_form.html.erb +27 -0
- data/app/views/blazer/uploads/edit.html.erb +3 -0
- data/app/views/blazer/uploads/index.html.erb +55 -0
- data/app/views/blazer/uploads/new.html.erb +3 -0
- data/config/routes.rb +5 -0
- data/lib/blazer.rb +20 -0
- data/lib/blazer/adapters/influxdb_adapter.rb +45 -0
- data/lib/blazer/adapters/sql_adapter.rb +1 -1
- data/lib/blazer/result.rb +2 -21
- data/lib/blazer/version.rb +1 -1
- data/lib/generators/blazer/templates/config.yml.tt +6 -0
- data/lib/generators/blazer/templates/install.rb.tt +4 -3
- data/lib/generators/blazer/templates/uploads.rb.tt +10 -0
- data/lib/generators/blazer/uploads_generator.rb +18 -0
- data/lib/tasks/blazer.rake +9 -0
- data/licenses/LICENSE-ace.txt +24 -0
- data/licenses/LICENSE-bootstrap.txt +21 -0
- data/licenses/LICENSE-chart.js.txt +9 -0
- data/licenses/LICENSE-chartkick.js.txt +22 -0
- data/licenses/LICENSE-daterangepicker.txt +21 -0
- data/licenses/LICENSE-fuzzysearch.txt +20 -0
- data/licenses/LICENSE-highlight.js.txt +29 -0
- data/licenses/LICENSE-jquery-ujs.txt +20 -0
- data/licenses/LICENSE-jquery.txt +20 -0
- data/licenses/LICENSE-moment-timezone.txt +20 -0
- data/licenses/LICENSE-moment.txt +22 -0
- data/licenses/LICENSE-selectize.txt +202 -0
- data/licenses/LICENSE-sortable.txt +21 -0
- data/licenses/LICENSE-stickytableheaders.txt +20 -0
- data/licenses/LICENSE-stupidtable.txt +19 -0
- data/licenses/LICENSE-vue.txt +21 -0
- metadata +33 -7
@@ -96,12 +96,12 @@ module Blazer
|
|
96
96
|
# when permitted parameters are passed in Rails 6,
|
97
97
|
# they appear to be added as GET parameters
|
98
98
|
# root_url(params.permit(:host))
|
99
|
-
|
99
|
+
UNPERMITTED_KEYS = [:controller, :action, :id, :host, :query, :dashboard, :query_id, :query_ids, :table_names, :authenticity_token, :utf8, :_method, :commit, :statement, :data_source, :name, :fork_query_id, :blazer, :run_id, :script_name, :original_script_name]
|
100
100
|
|
101
|
-
# remove
|
101
|
+
# remove unpermitted keys from both params and permitted keys for better sleep
|
102
102
|
def variable_params(resource)
|
103
|
-
permitted_keys = resource.variables -
|
104
|
-
params.except(*
|
103
|
+
permitted_keys = resource.variables - UNPERMITTED_KEYS.map(&:to_s)
|
104
|
+
params.except(*UNPERMITTED_KEYS).slice(*permitted_keys).permit!
|
105
105
|
end
|
106
106
|
helper_method :variable_params
|
107
107
|
|
@@ -21,8 +21,11 @@ module Blazer
|
|
21
21
|
|
22
22
|
def show
|
23
23
|
@queries = @dashboard.dashboard_queries.order(:position).preload(:query).map(&:query)
|
24
|
+
@statements = []
|
24
25
|
@queries.each do |query|
|
25
|
-
|
26
|
+
statement = query.statement.dup
|
27
|
+
process_vars(statement, query.data_source)
|
28
|
+
@statements << statement
|
26
29
|
end
|
27
30
|
@bind_vars ||= []
|
28
31
|
|
@@ -51,7 +54,7 @@ module Blazer
|
|
51
54
|
|
52
55
|
def destroy
|
53
56
|
@dashboard.destroy
|
54
|
-
redirect_to
|
57
|
+
redirect_to root_path
|
55
58
|
end
|
56
59
|
|
57
60
|
def refresh
|
@@ -38,11 +38,18 @@ module Blazer
|
|
38
38
|
if params[:fork_query_id]
|
39
39
|
@query.statement ||= Blazer::Query.find(params[:fork_query_id]).try(:statement)
|
40
40
|
end
|
41
|
+
if params[:upload_id]
|
42
|
+
upload = Blazer::Upload.find(params[:upload_id])
|
43
|
+
upload_settings = Blazer.settings["uploads"]
|
44
|
+
@query.data_source ||= upload_settings["data_source"]
|
45
|
+
@query.statement ||= "SELECT * FROM #{upload.table_name} LIMIT 10"
|
46
|
+
end
|
41
47
|
end
|
42
48
|
|
43
49
|
def create
|
44
50
|
@query = Blazer::Query.new(query_params)
|
45
51
|
@query.creator = blazer_user if @query.respond_to?(:creator)
|
52
|
+
@query.status = "active" if @query.respond_to?(:status)
|
46
53
|
|
47
54
|
if @query.save
|
48
55
|
redirect_to query_path(@query, variable_params(@query))
|
@@ -64,6 +71,8 @@ module Blazer
|
|
64
71
|
@sql_errors << error if error
|
65
72
|
end
|
66
73
|
|
74
|
+
@query.update!(status: "active") if @query.try(:status) == "archived"
|
75
|
+
|
67
76
|
Blazer.transform_statement.call(data_source, @statement) if Blazer.transform_statement
|
68
77
|
end
|
69
78
|
|
@@ -279,7 +288,7 @@ module Blazer
|
|
279
288
|
@queries = queries_by_ids(Blazer::Audit.where(user_id: blazer_user.id).order(created_at: :desc).limit(500).pluck(:query_id).uniq)
|
280
289
|
else
|
281
290
|
@queries = @queries.limit(limit) if limit
|
282
|
-
@queries = @queries.order(:name)
|
291
|
+
@queries = @queries.active.order(:name)
|
283
292
|
end
|
284
293
|
@queries = @queries.to_a
|
285
294
|
|
@@ -300,7 +309,7 @@ module Blazer
|
|
300
309
|
end
|
301
310
|
|
302
311
|
def queries_by_ids(favorite_query_ids)
|
303
|
-
queries = Blazer::Query.named.where(id: favorite_query_ids)
|
312
|
+
queries = Blazer::Query.active.named.where(id: favorite_query_ids)
|
304
313
|
queries = queries.includes(:creator) if Blazer.user_class
|
305
314
|
queries = queries.index_by(&:id)
|
306
315
|
favorite_query_ids.map { |query_id| queries[query_id] }.compact
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module Blazer
|
2
|
+
class UploadsController < BaseController
|
3
|
+
before_action :ensure_uploads
|
4
|
+
before_action :set_upload, only: [:show, :edit, :update, :destroy]
|
5
|
+
|
6
|
+
def index
|
7
|
+
@uploads = Blazer::Upload.order(:table)
|
8
|
+
end
|
9
|
+
|
10
|
+
def new
|
11
|
+
@upload = Blazer::Upload.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def create
|
15
|
+
@upload = Blazer::Upload.new(upload_params)
|
16
|
+
# use creator_id instead of creator
|
17
|
+
# since we setup association without checking if column exists
|
18
|
+
@upload.creator = blazer_user if @upload.respond_to?(:creator_id=) && blazer_user
|
19
|
+
|
20
|
+
success = params.require(:upload).key?(:file)
|
21
|
+
if success
|
22
|
+
Blazer::Upload.transaction do
|
23
|
+
success = @upload.save
|
24
|
+
if success
|
25
|
+
begin
|
26
|
+
update_file(@upload)
|
27
|
+
rescue CSV::MalformedCSVError, Blazer::UploadError => e
|
28
|
+
@upload.errors.add(:base, e.message)
|
29
|
+
success = false
|
30
|
+
raise ActiveRecord::Rollback
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
else
|
35
|
+
@upload.errors.add(:base, "File can't be blank")
|
36
|
+
end
|
37
|
+
|
38
|
+
if success
|
39
|
+
redirect_to upload_path(@upload)
|
40
|
+
else
|
41
|
+
render_errors @upload
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def show
|
46
|
+
redirect_to new_query_path(upload_id: @upload.id)
|
47
|
+
end
|
48
|
+
|
49
|
+
def edit
|
50
|
+
end
|
51
|
+
|
52
|
+
def update
|
53
|
+
original_table = @upload.table
|
54
|
+
@upload.assign_attributes(upload_params)
|
55
|
+
|
56
|
+
success = nil
|
57
|
+
Blazer::Upload.transaction do
|
58
|
+
success = @upload.save
|
59
|
+
if success
|
60
|
+
if params.require(:upload).key?(:file)
|
61
|
+
begin
|
62
|
+
update_file(@upload, drop: original_table)
|
63
|
+
rescue CSV::MalformedCSVError, Blazer::UploadError => e
|
64
|
+
@upload.errors.add(:base, e.message)
|
65
|
+
success = false
|
66
|
+
raise ActiveRecord::Rollback
|
67
|
+
end
|
68
|
+
elsif @upload.table != original_table
|
69
|
+
Blazer.uploads_connection.execute("ALTER TABLE #{Blazer.uploads_table_name(original_table)} RENAME TO #{Blazer.uploads_connection.quote_table_name(@upload.table)}")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
if success
|
75
|
+
redirect_to upload_path(@upload)
|
76
|
+
else
|
77
|
+
render_errors @upload
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def destroy
|
82
|
+
Blazer.uploads_connection.execute("DROP TABLE IF EXISTS #{@upload.table_name}")
|
83
|
+
@upload.destroy
|
84
|
+
redirect_to uploads_path
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def update_file(upload, drop: nil)
|
90
|
+
file = params.require(:upload).fetch(:file)
|
91
|
+
raise Blazer::UploadError, "File is not a CSV" if file.content_type != "text/csv"
|
92
|
+
raise Blazer::UploadError, "File is too large (maximum is 100MB)" if file.size > 100.megabytes
|
93
|
+
|
94
|
+
contents = file.read
|
95
|
+
rows = CSV.parse(contents, converters: %i[numeric date date_time])
|
96
|
+
|
97
|
+
# friendly column names
|
98
|
+
columns = rows.shift.map { |v| v.to_s.encode("UTF-8").gsub("%", " pct ").parameterize.gsub("-", "_") }
|
99
|
+
duplicate_column = columns.find { |c| columns.count(c) > 1 }
|
100
|
+
raise Blazer::UploadError, "Duplicate column name: #{duplicate_column}" if duplicate_column
|
101
|
+
|
102
|
+
column_types =
|
103
|
+
columns.size.times.map do |i|
|
104
|
+
values = rows.map { |r| r[i] }.uniq.compact
|
105
|
+
if values.all? { |v| v.is_a?(Integer) && v >= -9223372036854775808 && v <= 9223372036854775807 }
|
106
|
+
"bigint"
|
107
|
+
elsif values.all? { |v| v.is_a?(Numeric) }
|
108
|
+
"decimal"
|
109
|
+
elsif values.all? { |v| v.is_a?(DateTime) }
|
110
|
+
"timestamptz"
|
111
|
+
elsif values.all? { |v| v.is_a?(Date) }
|
112
|
+
"date"
|
113
|
+
else
|
114
|
+
"text"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
begin
|
119
|
+
# maybe SET LOCAL statement_timeout = '30s'
|
120
|
+
# maybe regenerate CSV in Ruby to ensure consistent parsing
|
121
|
+
Blazer.uploads_connection.transaction do
|
122
|
+
Blazer.uploads_connection.execute("DROP TABLE IF EXISTS #{Blazer.uploads_table_name(drop)}") if drop
|
123
|
+
Blazer.uploads_connection.execute("CREATE TABLE #{upload.table_name} (#{columns.map.with_index { |c, i| "#{Blazer.uploads_connection.quote_column_name(c)} #{column_types[i]}" }.join(", ")})")
|
124
|
+
Blazer.uploads_connection.raw_connection.copy_data("COPY #{upload.table_name} FROM STDIN CSV HEADER") do
|
125
|
+
Blazer.uploads_connection.raw_connection.put_copy_data(contents)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
rescue ActiveRecord::StatementInvalid => e
|
129
|
+
raise Blazer::UploadError, "Table already exists" if e.message.include?("PG::DuplicateTable")
|
130
|
+
raise e
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def upload_params
|
135
|
+
params.require(:upload).except(:file).permit(:table, :description)
|
136
|
+
end
|
137
|
+
|
138
|
+
def set_upload
|
139
|
+
@upload = Blazer::Upload.find(params[:id])
|
140
|
+
end
|
141
|
+
|
142
|
+
# routes aren't added, but also check here
|
143
|
+
def ensure_uploads
|
144
|
+
render plain: "Uploads not enabled" unless Blazer.uploads?
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -62,7 +62,7 @@ module Blazer
|
|
62
62
|
|
63
63
|
# checks shouldn't have variables, but in any case,
|
64
64
|
# avoid passing variable params to url helpers
|
65
|
-
# (known unsafe parameters are removed, but
|
65
|
+
# (known unsafe parameters are removed, but still not ideal)
|
66
66
|
def self.query_url(id)
|
67
67
|
Blazer::Engine.routes.url_helpers.query_url(id, ActionMailer::Base.default_url_options)
|
68
68
|
end
|
data/app/models/blazer/query.rb
CHANGED
@@ -0,0 +1,11 @@
|
|
1
|
+
module Blazer
|
2
|
+
class Upload < Record
|
3
|
+
belongs_to :creator, optional: true, class_name: Blazer.user_class.to_s if Blazer.user_class
|
4
|
+
|
5
|
+
validates :table, presence: true, uniqueness: true, format: {with: /\A[a-z0-9_]+\z/, message: "can only contain lowercase letters, numbers, and underscores"}, length: {maximum: 63}
|
6
|
+
|
7
|
+
def table_name
|
8
|
+
Blazer.uploads_table_name(table)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -6,6 +6,9 @@
|
|
6
6
|
</button>
|
7
7
|
<ul class="dropdown-menu">
|
8
8
|
<li><%= link_to "Checks", checks_path %></li>
|
9
|
+
<% if Blazer.uploads? %>
|
10
|
+
<li><%= link_to "Uploads", uploads_path %></li>
|
11
|
+
<% end %>
|
9
12
|
<li role="separator" class="divider"></li>
|
10
13
|
<li><%= link_to "New Query", new_query_path %></li>
|
11
14
|
<li><%= link_to "New Dashboard", new_dashboard_path %></li>
|
@@ -42,6 +42,7 @@
|
|
42
42
|
singleDatePicker: true,
|
43
43
|
locale: {format: format},
|
44
44
|
autoUpdateInput: false,
|
45
|
+
autoApply: true,
|
45
46
|
startDate: input.val().length > 0 ? moment.tz(input.val(), timeZone) : now
|
46
47
|
})
|
47
48
|
// hack to start with empty date
|
@@ -95,7 +96,8 @@
|
|
95
96
|
},
|
96
97
|
startDate: dateStr(29),
|
97
98
|
endDate: dateStr(),
|
98
|
-
opens: "right"
|
99
|
+
opens: "right",
|
100
|
+
alwaysShowCalendars: true
|
99
101
|
},
|
100
102
|
function(start, end) {
|
101
103
|
setTimeInputs(start, end)
|
@@ -13,7 +13,7 @@
|
|
13
13
|
<%= f.select :query_id, [], {include_blank: true} %>
|
14
14
|
</div>
|
15
15
|
<script>
|
16
|
-
<%= blazer_js_var "queries", Blazer::Query.named.order(:name).select("id, name").map { |q| {text: q.name, value: q.id} } %>
|
16
|
+
<%= blazer_js_var "queries", Blazer::Query.active.named.order(:name).select("id, name").map { |q| {text: q.name, value: q.id} } %>
|
17
17
|
<%= blazer_js_var "items", [@check.query_id].compact %>
|
18
18
|
|
19
19
|
$("#check_query_id").selectize({options: queries, items: items, highlight: false, maxOptions: 100}).parents(".hide").removeClass("hide");
|
@@ -10,6 +10,9 @@
|
|
10
10
|
</button>
|
11
11
|
<ul class="dropdown-menu">
|
12
12
|
<li><%= link_to "Home", root_path %></li>
|
13
|
+
<% if Blazer.uploads? %>
|
14
|
+
<li><%= link_to "Uploads", uploads_path %></li>
|
15
|
+
<% end %>
|
13
16
|
<li role="separator" class="divider"></li>
|
14
17
|
<li><%= link_to "New Query", new_query_path %></li>
|
15
18
|
<li><%= link_to "New Dashboard", new_dashboard_path %></li>
|
@@ -31,7 +31,7 @@
|
|
31
31
|
<% end %>
|
32
32
|
|
33
33
|
<script>
|
34
|
-
<%= blazer_js_var "queries", Blazer::Query.named.order(:name).select("id, name").map { |q| {text: q.name, value: q.id} } %>
|
34
|
+
<%= blazer_js_var "queries", Blazer::Query.active.named.order(:name).select("id, name").map { |q| {text: q.name, value: q.id} } %>
|
35
35
|
<%= blazer_js_var "dashboardQueries", @queries || @dashboard.dashboard_queries.order(:position).map(&:query) %>
|
36
36
|
|
37
37
|
var app = new Vue({
|
@@ -39,7 +39,7 @@
|
|
39
39
|
</div>
|
40
40
|
</div>
|
41
41
|
<script>
|
42
|
-
<%= blazer_js_var "data", {statement:
|
42
|
+
<%= blazer_js_var "data", {statement: @statements[i], query_id: query.id, data_source: query.data_source, only_chart: true} %>
|
43
43
|
|
44
44
|
runQuery(data, function (data) {
|
45
45
|
$("#chart-<%= i %>").html(data)
|
@@ -19,6 +19,9 @@
|
|
19
19
|
</button>
|
20
20
|
<ul class="dropdown-menu">
|
21
21
|
<li><%= link_to "Checks", checks_path %></li>
|
22
|
+
<% if Blazer.uploads? %>
|
23
|
+
<li><%= link_to "Uploads", uploads_path %></li>
|
24
|
+
<% end %>
|
22
25
|
<li role="separator" class="divider"></li>
|
23
26
|
<li><%= link_to "New Dashboard", new_dashboard_path %></li>
|
24
27
|
<li><%= link_to "New Check", new_check_path %></li>
|
@@ -107,13 +107,13 @@
|
|
107
107
|
</script>
|
108
108
|
<% elsif chart_type == "line" %>
|
109
109
|
<% chart_data = @columns[1..-1].each_with_index.map{ |k, i| {name: blazer_series_name(k), data: @rows.map{ |r| [r[0], r[i + 1]] }, library: series_library[i]} } %>
|
110
|
-
<%= line_chart chart_data, chart_options %>
|
110
|
+
<%= line_chart chart_data, **chart_options %>
|
111
111
|
<% elsif chart_type == "line2" %>
|
112
|
-
<%= line_chart @rows.group_by { |r| v = r[1]; (@boom[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: blazer_series_name(name), data: v.map { |v2| [v2[0], v2[2]] }, library: series_library[i]} }, chart_options %>
|
112
|
+
<%= line_chart @rows.group_by { |r| v = r[1]; (@boom[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: blazer_series_name(name), data: v.map { |v2| [v2[0], v2[2]] }, library: series_library[i]} }, **chart_options %>
|
113
113
|
<% elsif chart_type == "pie" %>
|
114
|
-
<%= pie_chart @rows.map { |r| [(@boom[@columns[0]] || {})[r[0].to_s] || r[0], r[1]] }, chart_options %>
|
114
|
+
<%= pie_chart @rows.map { |r| [(@boom[@columns[0]] || {})[r[0].to_s] || r[0], r[1]] }, **chart_options %>
|
115
115
|
<% elsif chart_type == "bar" %>
|
116
|
-
<%= column_chart (values.size - 1).times.map { |i| name = @columns[i + 1]; {name: blazer_series_name(name), data: @rows.first(20).map { |r| [(@boom[@columns[0]] || {})[r[0].to_s] || r[0], r[i + 1]] } } }, chart_options %>
|
116
|
+
<%= column_chart (values.size - 1).times.map { |i| name = @columns[i + 1]; {name: blazer_series_name(name), data: @rows.first(20).map { |r| [(@boom[@columns[0]] || {})[r[0].to_s] || r[0], r[i + 1]] } } }, **chart_options %>
|
117
117
|
<% elsif chart_type == "bar2" %>
|
118
118
|
<% first_20 = @rows.group_by { |r| r[0] }.values.first(20).flatten(1) %>
|
119
119
|
<% labels = first_20.map { |r| r[0] }.uniq %>
|
@@ -123,7 +123,7 @@
|
|
123
123
|
<% first_20 << [l, s, 0] unless first_20.find { |r| r[0] == l && r[1] == s } %>
|
124
124
|
<% end %>
|
125
125
|
<% end %>
|
126
|
-
<%= column_chart first_20.group_by { |r| v = r[1]; (@boom[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: blazer_series_name(name), data: v.sort_by { |r2| labels.index(r2[0]) }.map { |v2| v3 = v2[0]; [(@boom[@columns[0]] || {})[v3.to_s] || v3, v2[2]] }} }, chart_options %>
|
126
|
+
<%= column_chart first_20.group_by { |r| v = r[1]; (@boom[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: blazer_series_name(name), data: v.sort_by { |r2| labels.index(r2[0]) }.map { |v2| v3 = v2[0]; [(@boom[@columns[0]] || {})[v3.to_s] || v3, v2[2]] }} }, **chart_options %>
|
127
127
|
<% elsif chart_type == "scatter" %>
|
128
128
|
<%= scatter_chart @rows, xtitle: @columns[0], ytitle: @columns[1], **chart_options %>
|
129
129
|
<% elsif @only_chart %>
|
@@ -56,7 +56,9 @@
|
|
56
56
|
$("#results").addClass("query-error").html(message)
|
57
57
|
}
|
58
58
|
|
59
|
-
|
59
|
+
<% data = variable_params(@query).merge(statement: @statement, query_id: @query.id, data_source: @query.data_source) %>
|
60
|
+
<% data.merge!(forecast: "t") if params[:forecast] %>
|
61
|
+
<%= blazer_js_var "data", data %>
|
60
62
|
|
61
63
|
runQuery(data, showRun, showError)
|
62
64
|
</script>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<%= form_for @upload, html: {class: "small-form"} do |f| %>
|
2
|
+
<% if @upload.errors.any? %>
|
3
|
+
<div class="alert alert-danger"><%= @upload.errors.full_messages.first %></div>
|
4
|
+
<% elsif !@upload.persisted? %>
|
5
|
+
<p>Create a database table from a CSV file. The table will be created in the <code><%= Blazer.settings["uploads"]["schema"] %></code> schema.</p>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<div class="form-group">
|
9
|
+
<%= f.label :table %>
|
10
|
+
<%= f.text_field :table, class: "form-control" %>
|
11
|
+
</div>
|
12
|
+
<div class="form-group">
|
13
|
+
<%= f.label :description %>
|
14
|
+
<%= f.text_area :description, placeholder: "Optional", style: "height: 60px;", class: "form-control" %>
|
15
|
+
</div>
|
16
|
+
<div class="form-group">
|
17
|
+
<%= f.label :file %>
|
18
|
+
<%= f.file_field :file, accept: "text/csv", style: "margin-top: 6px; margin-bottom: 21px;" %>
|
19
|
+
</div>
|
20
|
+
<p>
|
21
|
+
<% if @upload.persisted? %>
|
22
|
+
<%= link_to "Delete", upload_path(@upload), method: :delete, "data-confirm" => "Are you sure?", class: "btn btn-danger" %>
|
23
|
+
<% end %>
|
24
|
+
<%= f.submit "Save", class: "btn btn-success" %>
|
25
|
+
<%= link_to "Back", :back, class: "btn btn-link" %>
|
26
|
+
</p>
|
27
|
+
<% end %>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<% blazer_title "Uploads" %>
|
2
|
+
|
3
|
+
<div id="header">
|
4
|
+
<div class="pull-right" style="line-height: 34px;">
|
5
|
+
<div class="btn-group">
|
6
|
+
<%= link_to "New Upload", new_upload_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><%= link_to "Checks", checks_path %></li>
|
14
|
+
<li role="separator" class="divider"></li>
|
15
|
+
<li><%= link_to "New Query", new_query_path %></li>
|
16
|
+
<li><%= link_to "New Dashboard", new_dashboard_path %></li>
|
17
|
+
<li><%= link_to "New Check", new_check_path %></li>
|
18
|
+
</ul>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<input id="search" type="text" placeholder="Start typing a table or person" style="width: 300px; display: inline-block;" class="search form-control" />
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<table id="uploads" class="table">
|
26
|
+
<thead>
|
27
|
+
<tr>
|
28
|
+
<th>Table</th>
|
29
|
+
<th style="width: 60%;"></th>
|
30
|
+
<% if Blazer.user_class %>
|
31
|
+
<th style="width: 20%; text-align: right;">Mastermind</th>
|
32
|
+
<% end%>
|
33
|
+
</tr>
|
34
|
+
</thead>
|
35
|
+
<tbody>
|
36
|
+
<% @uploads.each do |upload| %>
|
37
|
+
<tr>
|
38
|
+
<td><%= link_to upload.table, edit_upload_path(upload) %></td>
|
39
|
+
<td><%= truncate(upload.description, length: 100, separator: " ") %></td>
|
40
|
+
<% if Blazer.user_class %>
|
41
|
+
<td class="creator"><%= blazer_user && upload.creator == blazer_user ? "You" : upload.creator.try(Blazer.user_name) %></td>
|
42
|
+
<% end %>
|
43
|
+
</tr>
|
44
|
+
<% end %>
|
45
|
+
</tbody>
|
46
|
+
</table>
|
47
|
+
|
48
|
+
<script>
|
49
|
+
$("#search").on("keyup", function() {
|
50
|
+
var value = $(this).val().toLowerCase()
|
51
|
+
$("#uploads tbody tr").filter( function() {
|
52
|
+
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
|
53
|
+
})
|
54
|
+
}).focus()
|
55
|
+
</script>
|