blazer 2.2.8 → 2.4.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +131 -168
  5. data/app/assets/stylesheets/blazer/application.css +4 -0
  6. data/app/controllers/blazer/base_controller.rb +8 -0
  7. data/app/controllers/blazer/dashboards_controller.rb +2 -0
  8. data/app/controllers/blazer/queries_controller.rb +119 -3
  9. data/app/controllers/blazer/uploads_controller.rb +147 -0
  10. data/app/models/blazer/query.rb +9 -2
  11. data/app/models/blazer/upload.rb +11 -0
  12. data/app/models/blazer/uploads_connection.rb +7 -0
  13. data/app/views/blazer/_nav.html.erb +3 -0
  14. data/app/views/blazer/checks/_form.html.erb +1 -1
  15. data/app/views/blazer/checks/index.html.erb +3 -0
  16. data/app/views/blazer/dashboards/_form.html.erb +1 -1
  17. data/app/views/blazer/dashboards/show.html.erb +1 -1
  18. data/app/views/blazer/queries/_caching.html.erb +16 -0
  19. data/app/views/blazer/queries/_cohorts.html.erb +48 -0
  20. data/app/views/blazer/queries/docs.html.erb +6 -0
  21. data/app/views/blazer/queries/home.html.erb +3 -0
  22. data/app/views/blazer/queries/run.html.erb +15 -17
  23. data/app/views/blazer/queries/show.html.erb +1 -1
  24. data/app/views/blazer/uploads/_form.html.erb +27 -0
  25. data/app/views/blazer/uploads/edit.html.erb +3 -0
  26. data/app/views/blazer/uploads/index.html.erb +55 -0
  27. data/app/views/blazer/uploads/new.html.erb +3 -0
  28. data/config/routes.rb +5 -0
  29. data/lib/blazer.rb +24 -0
  30. data/lib/blazer/adapters/base_adapter.rb +8 -0
  31. data/lib/blazer/adapters/druid_adapter.rb +3 -3
  32. data/lib/blazer/adapters/hive_adapter.rb +45 -0
  33. data/lib/blazer/adapters/ignite_adapter.rb +54 -0
  34. data/lib/blazer/adapters/spark_adapter.rb +9 -0
  35. data/lib/blazer/adapters/sql_adapter.rb +64 -1
  36. data/lib/blazer/data_source.rb +2 -2
  37. data/lib/blazer/version.rb +1 -1
  38. data/lib/generators/blazer/templates/config.yml.tt +6 -0
  39. data/lib/generators/blazer/templates/install.rb.tt +1 -0
  40. data/lib/generators/blazer/templates/uploads.rb.tt +10 -0
  41. data/lib/generators/blazer/uploads_generator.rb +18 -0
  42. data/lib/tasks/blazer.rake +9 -0
  43. metadata +18 -32
@@ -0,0 +1,9 @@
1
+ module Blazer
2
+ module Adapters
3
+ class SparkAdapter < HiveAdapter
4
+ def tables
5
+ client.execute("SHOW TABLES").map { |r| r["tableName"] }
6
+ end
7
+ end
8
+ end
9
+ end
@@ -27,7 +27,7 @@ module Blazer
27
27
  result = select_all("#{statement} /*#{comment}*/")
28
28
  columns = result.columns
29
29
  result.rows.each do |untyped_row|
30
- rows << (result.column_types.empty? ? untyped_row : columns.each_with_index.map { |c, i| untyped_row[i] ? result.column_types[c].send(:cast_value, untyped_row[i]) : untyped_row[i] })
30
+ rows << (result.column_types.empty? ? untyped_row : columns.each_with_index.map { |c, i| untyped_row[i] && result.column_types[c] ? result.column_types[c].send(:cast_value, untyped_row[i]) : untyped_row[i] })
31
31
  end
32
32
  end
33
33
  rescue => e
@@ -122,6 +122,67 @@ 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? || mysql?
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
+ tzname = Blazer.time_zone.tzinfo.name
135
+
136
+ if mysql?
137
+ time_sql = "CONVERT_TZ(cohorts.cohort_time, '+00:00', ?)"
138
+ case period
139
+ when "day"
140
+ date_sql = "CAST(DATE_FORMAT(#{time_sql}, '%Y-%m-%d') AS DATE)"
141
+ date_params = [tzname]
142
+ when "week"
143
+ date_sql = "CAST(DATE_FORMAT(#{time_sql} - INTERVAL ((5 + DAYOFWEEK(#{time_sql})) % 7) DAY, '%Y-%m-%d') AS DATE)"
144
+ date_params = [tzname, tzname]
145
+ else
146
+ date_sql = "CAST(DATE_FORMAT(#{time_sql}, '%Y-%m-01') AS DATE)"
147
+ date_params = [tzname]
148
+ end
149
+ bucket_sql = "CAST(CEIL(TIMESTAMPDIFF(SECOND, cohorts.cohort_time, query.conversion_time) / ?) AS INTEGER)"
150
+ else
151
+ date_sql = "date_trunc(?, cohorts.cohort_time::timestamptz AT TIME ZONE ?)::date"
152
+ date_params = [period, tzname]
153
+ bucket_sql = "CEIL(EXTRACT(EPOCH FROM query.conversion_time - cohorts.cohort_time) / ?)::int"
154
+ end
155
+
156
+ # WITH not an optimization fence in Postgres 12+
157
+ statement = <<~SQL
158
+ WITH query AS (
159
+ #{statement}
160
+ ),
161
+ cohorts AS (
162
+ SELECT user_id, MIN(#{cohort_column}) AS cohort_time FROM query
163
+ WHERE user_id IS NOT NULL AND #{cohort_column} IS NOT NULL
164
+ GROUP BY 1
165
+ )
166
+ SELECT
167
+ #{date_sql} AS period,
168
+ 0 AS bucket,
169
+ COUNT(DISTINCT cohorts.user_id)
170
+ FROM cohorts GROUP BY 1
171
+ UNION ALL
172
+ SELECT
173
+ #{date_sql} AS period,
174
+ #{bucket_sql} AS bucket,
175
+ COUNT(DISTINCT query.user_id)
176
+ FROM cohorts INNER JOIN query ON query.user_id = cohorts.user_id
177
+ WHERE query.conversion_time IS NOT NULL
178
+ AND query.conversion_time >= cohorts.cohort_time
179
+ #{cohort_column == "conversion_time" ? "AND query.conversion_time != cohorts.cohort_time" : ""}
180
+ GROUP BY 1, 2
181
+ SQL
182
+ params = [statement] + date_params + date_params + [days.to_i * 86400]
183
+ connection_model.send(:sanitize_sql_array, params)
184
+ end
185
+
125
186
  protected
126
187
 
127
188
  def select_all(statement, params = [])
@@ -165,6 +226,8 @@ module Blazer
165
226
  "public"
166
227
  elsif sqlserver?
167
228
  "dbo"
229
+ elsif connection_model.respond_to?(:connection_db_config)
230
+ connection_model.connection_db_config.database
168
231
  else
169
232
  connection_model.connection_config[:database]
170
233
  end
@@ -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
@@ -185,7 +185,7 @@ module Blazer
185
185
  def detect_adapter
186
186
  schema = settings["url"].to_s.split("://").first
187
187
  case schema
188
- when "mongodb", "presto", "cassandra"
188
+ when "mongodb", "presto", "cassandra", "ignite"
189
189
  schema
190
190
  else
191
191
  "sql"
@@ -1,3 +1,3 @@
1
1
  module Blazer
2
- VERSION = "2.2.8"
2
+ VERSION = "2.4.2"
3
3
  end
@@ -71,3 +71,9 @@ check_schedules:
71
71
 
72
72
  # enable map
73
73
  # mapbox_access_token: <%%= ENV["MAPBOX_ACCESS_TOKEN"] %>
74
+
75
+ # enable uploads
76
+ # uploads:
77
+ # url: <%%= ENV["BLAZER_UPLOADS_URL"] %>
78
+ # schema: uploads
79
+ # data_source: main
@@ -6,6 +6,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
6
6
  t.text :description
7
7
  t.text :statement
8
8
  t.string :data_source
9
+ t.string :status
9
10
  t.timestamps null: false
10
11
  end
11
12
 
@@ -0,0 +1,10 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :blazer_uploads do |t|
4
+ t.references :creator
5
+ t.string :table
6
+ t.text :description
7
+ t.timestamps null: false
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ require "rails/generators/active_record"
2
+
3
+ module Blazer
4
+ module Generators
5
+ class UploadsGenerator < Rails::Generators::Base
6
+ include ActiveRecord::Generators::Migration
7
+ source_root File.join(__dir__, "templates")
8
+
9
+ def copy_migration
10
+ migration_template "uploads.rb", "db/migrate/create_blazer_uploads.rb", migration_version: migration_version
11
+ end
12
+
13
+ def migration_version
14
+ "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -8,4 +8,13 @@ namespace :blazer do
8
8
  task send_failing_checks: :environment do
9
9
  Blazer.send_failing_checks
10
10
  end
11
+
12
+ desc "archive queries"
13
+ task archive_queries: :environment do
14
+ abort "Audits must be enabled to archive" unless Blazer.audit
15
+ abort "Missing status column - see https://github.com/ankane/blazer#23" unless Blazer::Query.column_names.include?("status")
16
+
17
+ viewed_query_ids = Blazer::Audit.where("created_at > ?", 90.days.ago).group(:query_id).count.keys.compact
18
+ Blazer::Query.active.where.not(id: viewed_query_ids).update_all(status: "archived")
19
+ end
11
20
  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.2.8
4
+ version: 2.4.2
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-01 00:00:00.000000000 Z
11
+ date: 2021-02-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -66,36 +66,8 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.1.1
69
- - !ruby/object:Gem::Dependency
70
- name: bundler
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: rake
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
69
  description:
98
- email: andrew@chartkick.com
70
+ email: andrew@ankane.org
99
71
  executables: []
100
72
  extensions: []
101
73
  extra_rdoc_files: []
@@ -145,6 +117,7 @@ files:
145
117
  - app/controllers/blazer/checks_controller.rb
146
118
  - app/controllers/blazer/dashboards_controller.rb
147
119
  - app/controllers/blazer/queries_controller.rb
120
+ - app/controllers/blazer/uploads_controller.rb
148
121
  - app/helpers/blazer/base_helper.rb
149
122
  - app/mailers/blazer/check_mailer.rb
150
123
  - app/mailers/blazer/slack_notifier.rb
@@ -155,6 +128,8 @@ files:
155
128
  - app/models/blazer/dashboard_query.rb
156
129
  - app/models/blazer/query.rb
157
130
  - app/models/blazer/record.rb
131
+ - app/models/blazer/upload.rb
132
+ - app/models/blazer/uploads_connection.rb
158
133
  - app/views/blazer/_nav.html.erb
159
134
  - app/views/blazer/_variables.html.erb
160
135
  - app/views/blazer/check_mailer/failing_checks.html.erb
@@ -167,6 +142,8 @@ files:
167
142
  - app/views/blazer/dashboards/edit.html.erb
168
143
  - app/views/blazer/dashboards/new.html.erb
169
144
  - app/views/blazer/dashboards/show.html.erb
145
+ - app/views/blazer/queries/_caching.html.erb
146
+ - app/views/blazer/queries/_cohorts.html.erb
170
147
  - app/views/blazer/queries/_form.html.erb
171
148
  - app/views/blazer/queries/docs.html.erb
172
149
  - app/views/blazer/queries/edit.html.erb
@@ -175,6 +152,10 @@ files:
175
152
  - app/views/blazer/queries/run.html.erb
176
153
  - app/views/blazer/queries/schema.html.erb
177
154
  - app/views/blazer/queries/show.html.erb
155
+ - app/views/blazer/uploads/_form.html.erb
156
+ - app/views/blazer/uploads/edit.html.erb
157
+ - app/views/blazer/uploads/index.html.erb
158
+ - app/views/blazer/uploads/new.html.erb
178
159
  - app/views/layouts/blazer/application.html.erb
179
160
  - config/routes.rb
180
161
  - lib/blazer.rb
@@ -185,6 +166,8 @@ files:
185
166
  - lib/blazer/adapters/drill_adapter.rb
186
167
  - lib/blazer/adapters/druid_adapter.rb
187
168
  - lib/blazer/adapters/elasticsearch_adapter.rb
169
+ - lib/blazer/adapters/hive_adapter.rb
170
+ - lib/blazer/adapters/ignite_adapter.rb
188
171
  - lib/blazer/adapters/influxdb_adapter.rb
189
172
  - lib/blazer/adapters/mongodb_adapter.rb
190
173
  - lib/blazer/adapters/neo4j_adapter.rb
@@ -192,6 +175,7 @@ files:
192
175
  - lib/blazer/adapters/salesforce_adapter.rb
193
176
  - lib/blazer/adapters/snowflake_adapter.rb
194
177
  - lib/blazer/adapters/soda_adapter.rb
178
+ - lib/blazer/adapters/spark_adapter.rb
195
179
  - lib/blazer/adapters/sql_adapter.rb
196
180
  - lib/blazer/data_source.rb
197
181
  - lib/blazer/detect_anomalies.R
@@ -203,6 +187,8 @@ files:
203
187
  - lib/generators/blazer/install_generator.rb
204
188
  - lib/generators/blazer/templates/config.yml.tt
205
189
  - lib/generators/blazer/templates/install.rb.tt
190
+ - lib/generators/blazer/templates/uploads.rb.tt
191
+ - lib/generators/blazer/uploads_generator.rb
206
192
  - lib/tasks/blazer.rake
207
193
  - licenses/LICENSE-ace.txt
208
194
  - licenses/LICENSE-bootstrap.txt
@@ -239,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
239
225
  - !ruby/object:Gem::Version
240
226
  version: '0'
241
227
  requirements: []
242
- rubygems_version: 3.1.4
228
+ rubygems_version: 3.2.3
243
229
  signing_key:
244
230
  specification_version: 4
245
231
  summary: Explore your data with SQL. Easily create charts and dashboards, and share