blazer 2.3.1 → 2.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71bd121d2bace5612677090372e36ebdd89eb09398bd7510ba38c666351c1eb4
4
- data.tar.gz: 4b109076d6af5aee545f4c19311b431feb68bc7e31f45e28704a99fe7692595b
3
+ metadata.gz: c9f173935d9c63e3537014420daf0af3017f6f9c51615d102fb6f0b4b3b45ba8
4
+ data.tar.gz: 8743bc1783f5181bd946d6263a70ac7ae02cbb26d3663b59c8915cdc9495e80f
5
5
  SHA512:
6
- metadata.gz: 035bcb0b2252cd10514b5ab3f28c78b4ae6b6b6b534d99948f20815076661cf7fe85200debcf64ab11c6e5307fc24c326daea76f361f6c474e8216b69ebbbe35
7
- data.tar.gz: 4838d7ee13b888d005602916d309825f716cd868287b0f1843aa92bf1a96d5f43168f22de3430c7b2ba6b4ab25fc71e1ee6bd6b45588729c0a1b44897d508052
6
+ metadata.gz: bfe97805e455f1ec96cc825563460a303006c6c379f9adfb3b24ed2f9f3b86f57f9600648a8fbc01a564ba62f78346582659dab9c121d239f91383211a3b8ad3
7
+ data.tar.gz: 52a1ce2a7a489105ba1c1ece23e412117de868bf6c50a07b968056b6ccbbe40d90031087c836f7831d04f12bd7d88593260a0a98bfdcb15659e1158c3fc070d6
@@ -1,3 +1,9 @@
1
+ ## 2.4.0 (2020-12-15)
2
+
3
+ - Added cohorts
4
+ - Fixed broken routes for some applications
5
+ - Forecasting and uploads are no longer experimental
6
+
1
7
  ## 2.3.1 (2020-11-23)
2
8
 
3
9
  - Improved column names for uploads
data/README.md CHANGED
@@ -25,6 +25,7 @@ Blazer is also available as a [Docker image](https://github.com/ankane/blazer-do
25
25
  - [Charts](#charts)
26
26
  - [Dashboards](#dashboards)
27
27
  - [Checks](#checks)
28
+ - [Cohorts](#cohorts)
28
29
  - [Anomaly Detection](#anomaly-detection)
29
30
  - [Forecasting](#forecasting)
30
31
  - [Uploads](#uploads)
@@ -388,6 +389,31 @@ SELECT * FROM ratings WHERE user_id IS NULL /* all ratings should have a user */
388
389
 
389
390
  Then create check with optional emails if you want to be notified. Emails are sent when a check starts failing, and when it starts passing again.
390
391
 
392
+ ## Cohorts
393
+
394
+ Create a cohort analysis from a simple SQL query. [Example](https://blazer.dokkuapp.com/queries/19-cohort-analysis-from-first-order)
395
+
396
+ Create a query with the comment `/* cohort analysis */`. The result should have columns named `user_id` and `conversion_time` and optionally `cohort_time`.
397
+
398
+ You can generate cohorts from the first conversion time:
399
+
400
+ ```sql
401
+ /* cohort analysis */
402
+ SELECT user_id, created_at AS conversion_time FROM orders
403
+ ```
404
+
405
+ (the first conversion isn’t counted in the first time period with this format)
406
+
407
+ Or from another time, like sign up:
408
+
409
+ ```sql
410
+ /* cohort analysis */
411
+ SELECT users.id AS user_id, orders.created_at AS conversion_time, users.created_at AS cohort_time
412
+ FROM users LEFT JOIN orders ON orders.user_id = users.id
413
+ ```
414
+
415
+ This feature requires PostgreSQL.
416
+
391
417
  ## Anomaly Detection
392
418
 
393
419
  Blazer supports two different approaches to anomaly detection.
@@ -456,7 +482,7 @@ Commit and deploy away. The first deploy may take a few minutes.
456
482
 
457
483
  ## Forecasting
458
484
 
459
- Blazer has experimental support for two different forecasting methods. [Example](https://blazer.dokkuapp.com/queries/18-forecast?forecast=t)
485
+ Blazer supports for two different forecasting methods. [Example](https://blazer.dokkuapp.com/queries/18-forecast?forecast=t)
460
486
 
461
487
  A forecast link will appear for queries that return 2 columns with types timestamp and numeric.
462
488
 
@@ -492,7 +518,7 @@ forecasting: trend
492
518
 
493
519
  ## Uploads
494
520
 
495
- Blazer has experimental support for creating database tables from CSV files. [Example](https://blazer.dokkuapp.com/uploads)
521
+ Creating database tables from CSV files. [Example](https://blazer.dokkuapp.com/uploads)
496
522
 
497
523
  Run:
498
524
 
@@ -657,8 +683,6 @@ data_sources:
657
683
 
658
684
  ### InfluxDB
659
685
 
660
- *Experimental*
661
-
662
686
  Add [influxdb](https://github.com/influxdata/influxdb-ruby) to your Gemfile and set:
663
687
 
664
688
  ```yml
@@ -692,8 +716,6 @@ data_sources:
692
716
 
693
717
  ### Neo4j
694
718
 
695
- *Experimental*
696
-
697
719
  Add [neo4j-core](https://github.com/neo4jrb/neo4j-core) to your Gemfile and set:
698
720
 
699
721
  ```yml
@@ -735,8 +757,6 @@ data_sources:
735
757
 
736
758
  ### Salesforce
737
759
 
738
- *Experimental*
739
-
740
760
  Add [restforce](https://github.com/restforce/restforce) to your Gemfile and set:
741
761
 
742
762
  ```yml
@@ -760,8 +780,6 @@ Supports [SOQL](https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta
760
780
 
761
781
  ### Socrata Open Data API (SODA)
762
782
 
763
- *Experimental*
764
-
765
783
  Set:
766
784
 
767
785
  ```yml
@@ -16,6 +16,10 @@ body {
16
16
  padding-bottom: 15px;
17
17
  }
18
18
 
19
+ table.results-table {
20
+ margin-bottom: 0;
21
+ }
22
+
19
23
  .results-table th {
20
24
  cursor: pointer;
21
25
  }
@@ -6,6 +6,8 @@ module Blazer
6
6
  skip_after_action(*filters, raise: false)
7
7
  skip_around_action(*filters, raise: false)
8
8
 
9
+ clear_helpers
10
+
9
11
  protect_from_forgery with: :exception
10
12
 
11
13
  if ENV["BLAZER_PASSWORD"]
@@ -65,6 +67,12 @@ module Blazer
65
67
  end
66
68
  end
67
69
 
70
+ def add_cohort_analysis_vars
71
+ @bind_vars << "cohort_period" unless @bind_vars.include?("cohort_period")
72
+ @smart_vars["cohort_period"] = ["day", "week", "month"]
73
+ params[:cohort_period] ||= "week"
74
+ end
75
+
68
76
  def parse_smart_variables(var, data_source)
69
77
  smart_var_data_source =
70
78
  ([data_source] + Array(data_source.settings["inherit_smart_settings"]).map { |ds| Blazer.data_sources[ds] }).find { |ds| ds.smart_variables[var] }
@@ -39,6 +39,8 @@ module Blazer
39
39
  @sql_errors << error if error
40
40
  end
41
41
  end
42
+
43
+ add_cohort_analysis_vars if @queries.any?(&:cohort_analysis?)
42
44
  end
43
45
 
44
46
  def edit
@@ -74,6 +74,8 @@ module Blazer
74
74
  @query.update!(status: "active") if @query.try(:status) == "archived"
75
75
 
76
76
  Blazer.transform_statement.call(data_source, @statement) if Blazer.transform_statement
77
+
78
+ add_cohort_analysis_vars if @query.cohort_analysis?
77
79
  end
78
80
 
79
81
  def edit
@@ -81,6 +83,8 @@ module Blazer
81
83
 
82
84
  def run
83
85
  @statement = params[:statement]
86
+ # before process_vars
87
+ @cohort_analysis = Query.new(statement: @statement).cohort_analysis?
84
88
  data_source = params[:data_source]
85
89
  process_vars(@statement, data_source)
86
90
  @only_chart = params[:only_chart]
@@ -89,6 +93,8 @@ module Blazer
89
93
  data_source = @query.data_source if @query && @query.data_source
90
94
  @data_source = Blazer.data_sources[data_source]
91
95
 
96
+ run_cohort_analysis if @cohort_analysis
97
+
92
98
  # ensure viewable
93
99
  if !(@query || Query.new(data_source: @data_source.id)).viewable?(blazer_user)
94
100
  render_forbidden
@@ -164,6 +170,7 @@ module Blazer
164
170
  @statement = @query.statement.dup
165
171
  process_vars(@statement, @query.data_source)
166
172
  Blazer.transform_statement.call(data_source, @statement) if Blazer.transform_statement
173
+ @statement = cohort_analysis_statement(data_source, @statement) if @query.cohort_analysis?
167
174
  data_source.clear_cache(@statement)
168
175
  redirect_to query_path(@query, variable_params(@query))
169
176
  end
@@ -241,7 +248,6 @@ module Blazer
241
248
  end
242
249
  end
243
250
 
244
- @filename = @query.name.parameterize if @query
245
251
  @min_width_types = @columns.each_with_index.select { |c, i| @first_row[i].is_a?(Time) || @first_row[i].is_a?(String) || @data_source.smart_columns[c] }.map(&:last)
246
252
 
247
253
  @boom = @result.boom if @result
@@ -268,6 +274,8 @@ module Blazer
268
274
  end
269
275
  end
270
276
 
277
+ render_cohort_analysis if @cohort_analysis && !@error
278
+
271
279
  respond_to do |format|
272
280
  format.html do
273
281
  render layout: false
@@ -352,5 +360,104 @@ module Blazer
352
360
  def blazer_run_id
353
361
  params[:run_id].to_s.gsub(/[^a-z0-9\-]/i, "")
354
362
  end
363
+
364
+ def run_cohort_analysis
365
+ unless @data_source.supports_cohort_analysis?
366
+ @cohort_error = "This data source does not support cohort analysis"
367
+ end
368
+
369
+ @show_cohort_rows = !params[:query_id] || @cohort_error
370
+
371
+ unless @show_cohort_rows
372
+ @statement = cohort_analysis_statement(@data_source, @statement)
373
+ end
374
+ end
375
+
376
+ def cohort_analysis_statement(data_source, statement)
377
+ @cohort_period = params["cohort_period"] || "week"
378
+ @cohort_period = "week" unless ["day", "week", "month"].include?(@cohort_period)
379
+
380
+ # for now
381
+ @conversion_period = @cohort_period
382
+ @cohort_days =
383
+ case @cohort_period
384
+ when "day"
385
+ 1
386
+ when "week"
387
+ 7
388
+ when "month"
389
+ 30
390
+ end
391
+
392
+ data_source.cohort_analysis_statement(statement, period: @cohort_period, days: @cohort_days)
393
+ end
394
+
395
+ def render_cohort_analysis
396
+ if @show_cohort_rows
397
+ @cohort_analysis = false
398
+
399
+ @row_limit = 1000
400
+
401
+ # check results
402
+ unless @cohort_error
403
+ # check names
404
+ expected_columns = ["user_id", "conversion_time"]
405
+ missing_columns = expected_columns - @result.columns
406
+ if missing_columns.any?
407
+ @cohort_error = "Expected user_id and conversion_time columns"
408
+ end
409
+
410
+ # check types (user_id can be any type)
411
+ unless @cohort_error
412
+ column_types = @result.columns.zip(@result.column_types).to_h
413
+
414
+ if !column_types["cohort_time"].in?(["time", nil])
415
+ @cohort_error = "cohort_time must be time column"
416
+ elsif !column_types["conversion_time"].in?(["time", nil])
417
+ @cohort_error = "conversion_time must be time column"
418
+ end
419
+ end
420
+ end
421
+ else
422
+ @today = Blazer.time_zone.today
423
+ @min_cohort_date, @max_cohort_date = @result.rows.map { |r| r[0] }.minmax
424
+ @buckets = {}
425
+ @rows.each do |r|
426
+ @buckets[[r[0], r[1]]] = r[2]
427
+ end
428
+
429
+ @cohort_dates = []
430
+ current_date = @max_cohort_date
431
+ while current_date && current_date >= @min_cohort_date
432
+ @cohort_dates << current_date
433
+ current_date =
434
+ case @cohort_period
435
+ when "day"
436
+ current_date - 1
437
+ when "week"
438
+ current_date - 7
439
+ else
440
+ current_date.prev_month
441
+ end
442
+ end
443
+
444
+ num_cols = @cohort_dates.size
445
+ @columns = ["Cohort", "Users"] + num_cols.times.map { |i| "#{@conversion_period.titleize} #{i + 1}" }
446
+ rows = []
447
+ date_format = @cohort_period == "month" ? "%b %Y" : "%b %-e, %Y"
448
+ @cohort_dates.each do |date|
449
+ row = [date.strftime(date_format), @buckets[[date, 0]] || 0]
450
+
451
+ num_cols.times do |i|
452
+ if @today >= date + (@cohort_days * i)
453
+ row << (@buckets[[date, i + 1]] || 0)
454
+ end
455
+ end
456
+
457
+ rows << row
458
+ end
459
+ @rows = rows
460
+ end
461
+ end
355
462
  end
356
463
  end
@@ -35,7 +35,13 @@ module Blazer
35
35
  end
36
36
 
37
37
  def variables
38
- Blazer.extract_vars(statement)
38
+ variables = Blazer.extract_vars(statement)
39
+ variables += ["cohort_period"] if cohort_analysis?
40
+ variables
41
+ end
42
+
43
+ def cohort_analysis?
44
+ /\/\*\s*cohort analysis\s*\*\//i.match?(statement)
39
45
  end
40
46
  end
41
47
  end
@@ -39,7 +39,7 @@
39
39
  </div>
40
40
  </div>
41
41
  <script>
42
- <%= blazer_js_var "data", {statement: @statements[i], query_id: query.id, data_source: query.data_source, only_chart: true} %>
42
+ <%= blazer_js_var "data", {statement: @statements[i], query_id: query.id, data_source: query.data_source, only_chart: true, cohort_period: params[:cohort_period]} %>
43
43
 
44
44
  runQuery(data, function (data) {
45
45
  $("#chart-<%= i %>").html(data)
@@ -0,0 +1,16 @@
1
+ <% if @cached_at || @just_cached %>
2
+ <p class="text-muted" style="float: right;">
3
+ <% if @cached_at %>
4
+ Cached <%= time_ago_in_words(@cached_at, include_seconds: true) %> ago
5
+ <% elsif params[:query_id] %>
6
+ Cached just now
7
+ <% if @data_source.cache_mode == "slow" %>
8
+ (over <%= "%g" % @data_source.cache_slow_threshold %>s)
9
+ <% end %>
10
+ <% end %>
11
+
12
+ <% if @query && params[:query_id] %>
13
+ <%= link_to "Refresh", refresh_query_path(@query, variable_params(@query)), method: :post %>
14
+ <% end %>
15
+ </p>
16
+ <% end %>
@@ -0,0 +1,48 @@
1
+ <% unless @only_chart %>
2
+ <%= render partial: "caching" %>
3
+ <p class="text-muted" style="margin-bottom: 10px;">
4
+ <%= pluralize(@rows.size, "cohort") %>
5
+ </p>
6
+ <% end %>
7
+ <% if @rows.any? %>
8
+ <div class="results-container">
9
+ <table class="table results-table">
10
+ <thead>
11
+ <tr>
12
+ <th style="min-width: 100px;">Cohort</th>
13
+ <% 12.times do |i| %>
14
+ <th style="width: 7.5%; text-align: right;"><%= @conversion_period.titleize %> <%= i + 1 %></th>
15
+ <% end %>
16
+ </tr>
17
+ </thead>
18
+ <tbody>
19
+ <% @rows.each do |row| %>
20
+ <tr>
21
+ <td>
22
+ <%= row[0] %>
23
+ <div style="font-size: 12px; color: #999;"><%= row[1] == 1 ? "1 user" : "#{number_with_delimiter(row[1])} users" %></div>
24
+ </td>
25
+ <% 12.times do |i| %>
26
+ <td style="text-align: right;">
27
+ <% num = row[i + 2] %>
28
+ <% if num %>
29
+ <% denom = row[1] %>
30
+ <% if denom > 0 %>
31
+ <%= (100.0 * num / denom).round %>%
32
+ <% else %>
33
+ -
34
+ <% end %>
35
+ <div style="font-size: 12px; color: #999;"><%= number_with_delimiter(num) %></div>
36
+ <% else %>
37
+ -
38
+ <% end %>
39
+ </td>
40
+ <% end %>
41
+ </tr>
42
+ <% end %>
43
+ </tbody>
44
+ </table>
45
+ </div>
46
+ <% elsif @only_chart %>
47
+ <p class="text-muted">No cohorts</p>
48
+ <% end %>
@@ -129,3 +129,9 @@
129
129
  </table>
130
130
 
131
131
  <p>Use the column name <code>target</code> to draw a line for goals.</p>
132
+
133
+ <% if @data_source.supports_cohort_analysis? %>
134
+ <h2>Cohort Analysis</h2>
135
+
136
+ <p>Create a query with the comment <code>/* cohort analysis */</code>. The result should have columns named <code>user_id</code> and <code>conversion_time</code> and optionally <code>cohort_time</code>.</p>
137
+ <% end %>
@@ -6,25 +6,20 @@
6
6
  <% else %>
7
7
  <div class="alert alert-info">Can’t preview queries with variables...yet!</div>
8
8
  <% end %>
9
+ <% elsif @cohort_analysis %>
10
+ <% if @cohort_error %>
11
+ <div class="alert alert-info"><%= @cohort_error %></div>
12
+ <% else %>
13
+ <%= render partial: "cohorts" %>
14
+ <% end %>
9
15
  <% else %>
10
16
  <% unless @only_chart %>
11
- <% if @cached_at || @just_cached %>
12
- <p class="text-muted" style="float: right;">
13
- <% if @cached_at %>
14
- Cached <%= time_ago_in_words(@cached_at, include_seconds: true) %> ago
15
- <% elsif params[:query_id] %>
16
- Cached just now
17
- <% if @data_source.cache_mode == "slow" %>
18
- (over <%= "%g" % @data_source.cache_slow_threshold %>s)
19
- <% end %>
20
- <% end %>
21
-
22
- <% if @query && params[:query_id] %>
23
- <%= link_to "Refresh", refresh_query_path(@query, variable_params(@query)), method: :post %>
24
- <% end %>
25
- </p>
26
- <% end %>
17
+ <%= render partial: "caching" %>
27
18
  <p class="text-muted" style="margin-bottom: 10px;">
19
+ <% if @row_limit && @rows.size > @row_limit %>
20
+ First
21
+ <% @rows = @rows.first(@row_limit) %>
22
+ <% end %>
28
23
  <%= pluralize(@rows.size, "row") %>
29
24
 
30
25
  <% @checks.select(&:state).each do |check| %>
@@ -43,6 +38,9 @@
43
38
  <% if @forecast_error %>
44
39
  <div class="alert alert-danger"><%= @forecast_error %></div>
45
40
  <% end %>
41
+ <% if @cohort_error %>
42
+ <div class="alert alert-info"><%= @cohort_error %></div>
43
+ <% end %>
46
44
  <% if @rows.any? %>
47
45
  <% values = @rows.first %>
48
46
  <% chart_id = SecureRandom.hex %>
@@ -147,7 +145,7 @@
147
145
  <% elsif @columns == ["PLAN"] && @data_source.adapter == "druid" %>
148
146
  <pre><code><%= @rows[0][0] %></code></pre>
149
147
  <% else %>
150
- <table class="table results-table" style="margin-bottom: 0;">
148
+ <table class="table results-table">
151
149
  <thead>
152
150
  <tr>
153
151
  <% @columns.each_with_index do |key, i| %>
@@ -14,7 +14,7 @@
14
14
  <%= link_to "Fork", new_query_path(variable_params(@query).merge(fork_query_id: @query.id, data_source: @query.data_source, name: @query.name)), class: "btn btn-info" %>
15
15
 
16
16
  <% if !@error && @success %>
17
- <%= button_to "Download", run_queries_path(query_id: @query.id, format: "csv", forecast: params[:forecast]), params: {statement: @statement}, class: "btn btn-primary" %>
17
+ <%= button_to "Download", run_queries_path(query_id: @query.id, format: "csv", forecast: params[:forecast], cohort_period: params[:cohort_period]), params: {statement: @statement}, class: "btn btn-primary" %>
18
18
  <% end %>
19
19
  </div>
20
20
  </div>
@@ -43,6 +43,14 @@ module Blazer
43
43
  true # optional
44
44
  end
45
45
 
46
+ def supports_cohort_analysis?
47
+ false # optional
48
+ end
49
+
50
+ def cohort_analysis_statement(statement, period:, days:)
51
+ # optional
52
+ end
53
+
46
54
  protected
47
55
 
48
56
  def settings
@@ -122,6 +122,47 @@ module Blazer
122
122
  !%w[CREATE ALTER UPDATE INSERT DELETE].include?(statement.split.first.to_s.upcase)
123
123
  end
124
124
 
125
+ def supports_cohort_analysis?
126
+ postgresql?
127
+ end
128
+
129
+ # TODO treat date columns as already in time zone
130
+ def cohort_analysis_statement(statement, period:, days:)
131
+ raise "Cohort analysis not supported" unless supports_cohort_analysis?
132
+
133
+ cohort_column = statement =~ /\bcohort_time\b/ ? "cohort_time" : "conversion_time"
134
+
135
+ # WITH not an optimization fence in Postgres 12+
136
+ statement = <<~SQL
137
+ WITH query AS (
138
+ #{statement}
139
+ ),
140
+ cohorts AS (
141
+ SELECT user_id, MIN(#{cohort_column}) AS cohort_time FROM query
142
+ WHERE user_id IS NOT NULL AND #{cohort_column} IS NOT NULL
143
+ GROUP BY 1
144
+ )
145
+ SELECT
146
+ date_trunc(?, cohorts.cohort_time::timestamptz AT TIME ZONE ?)::date AS period,
147
+ 0 AS bucket,
148
+ COUNT(DISTINCT cohorts.user_id)
149
+ FROM cohorts GROUP BY 1
150
+ UNION ALL
151
+ SELECT
152
+ date_trunc(?, cohorts.cohort_time::timestamptz AT TIME ZONE ?)::date AS period,
153
+ CEIL(EXTRACT(EPOCH FROM query.conversion_time - cohorts.cohort_time) / ?)::int AS bucket,
154
+ COUNT(DISTINCT query.user_id)
155
+ FROM cohorts INNER JOIN query ON query.user_id = cohorts.user_id
156
+ WHERE query.conversion_time IS NOT NULL
157
+ AND query.conversion_time >= cohorts.cohort_time
158
+ #{cohort_column == "conversion_time" ? "AND query.conversion_time != cohorts.cohort_time" : ""}
159
+ GROUP BY 1, 2
160
+ SQL
161
+ tzname = Blazer.time_zone.tzinfo.name
162
+ params = [statement, period, tzname, period, tzname, days.to_i * 86400]
163
+ connection_model.send(:sanitize_sql_array, params)
164
+ end
165
+
125
166
  protected
126
167
 
127
168
  def select_all(statement, params = [])
@@ -6,7 +6,7 @@ module Blazer
6
6
 
7
7
  attr_reader :id, :settings
8
8
 
9
- def_delegators :adapter_instance, :schema, :tables, :preview_statement, :reconnect, :cost, :explain, :cancel
9
+ def_delegators :adapter_instance, :schema, :tables, :preview_statement, :reconnect, :cost, :explain, :cancel, :supports_cohort_analysis?, :cohort_analysis_statement
10
10
 
11
11
  def initialize(id, settings)
12
12
  @id = id
@@ -1,3 +1,3 @@
1
1
  module Blazer
2
- VERSION = "2.3.1"
2
+ VERSION = "2.4.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blazer
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-24 00:00:00.000000000 Z
11
+ date: 2020-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -170,6 +170,8 @@ files:
170
170
  - app/views/blazer/dashboards/edit.html.erb
171
171
  - app/views/blazer/dashboards/new.html.erb
172
172
  - app/views/blazer/dashboards/show.html.erb
173
+ - app/views/blazer/queries/_caching.html.erb
174
+ - app/views/blazer/queries/_cohorts.html.erb
173
175
  - app/views/blazer/queries/_form.html.erb
174
176
  - app/views/blazer/queries/docs.html.erb
175
177
  - app/views/blazer/queries/edit.html.erb