queries_controller.rb 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. module Blazer
  2. class QueriesController < BaseController
  3. before_action :set_query, only: [:show, :edit, :update, :destroy, :refresh]
  4. def home
  5. set_queries(1000)
  6. @dashboards = Blazer::Dashboard.order(:name)
  7. @dashboards = @dashboards.includes(:creator) if Blazer.user_class
  8. @dashboards =
  9. @dashboards.map do |d|
  10. {
  11. name: "<strong>#{view_context.link_to(d.name, d)}</strong>",
  12. creator: blazer_user && d.try(:creator) == blazer_user ? "You" : d.try(:creator).try(Blazer.user_name),
  13. hide: d.name.gsub(/\s+/, ""),
  14. vars: nil
  15. }
  16. end
  17. end
  18. def index
  19. set_queries
  20. render json: @queries
  21. end
  22. def new
  23. @query = Blazer::Query.new(
  24. data_source: params[:data_source],
  25. name: params[:name]
  26. )
  27. if params[:fork_query_id]
  28. @query.statement ||= Blazer::Query.find(params[:fork_query_id]).try(:statement)
  29. end
  30. end
  31. def create
  32. @query = Blazer::Query.new(query_params)
  33. @query.creator = blazer_user if @query.respond_to?(:creator)
  34. if @query.save
  35. redirect_to query_path(@query, variable_params)
  36. else
  37. render :new
  38. end
  39. end
  40. def show
  41. @statement = @query.statement.dup
  42. process_vars(@statement, @query.data_source)
  43. @smart_vars = {}
  44. @sql_errors = []
  45. data_source = Blazer.data_sources[@query.data_source]
  46. @bind_vars.each do |var|
  47. query = data_source.smart_variables[var]
  48. if query
  49. result = data_source.run_statement(query)
  50. @smart_vars[var] = result.rows.map { |v| v.reverse }
  51. @sql_errors << result.error if result.error
  52. end
  53. end
  54. Blazer.transform_statement.call(data_source, @statement) if Blazer.transform_statement
  55. end
  56. def edit
  57. end
  58. def run
  59. @statement = params[:statement]
  60. data_source = params[:data_source]
  61. process_vars(@statement, data_source)
  62. @only_chart = params[:only_chart]
  63. @run_id = blazer_params[:run_id]
  64. @query = Query.find_by(id: params[:query_id]) if params[:query_id]
  65. data_source = @query.data_source if @query && @query.data_source
  66. @data_source = Blazer.data_sources[data_source]
  67. if @run_id
  68. @timestamp = blazer_params[:timestamp].to_i
  69. @result = @data_source.run_results(@run_id)
  70. @success = !@result.nil?
  71. if @success
  72. @data_source.delete_results(@run_id)
  73. @columns = @result.columns
  74. @rows = @result.rows
  75. @error = @result.error
  76. @just_cached = !@result.error && @result.cached_at.present?
  77. @cached_at = nil
  78. params[:data_source] = nil
  79. render_run
  80. elsif Time.now > Time.at(@timestamp + (@data_source.timeout || 120).to_i)
  81. # timed out
  82. @error = Blazer::TIMEOUT_MESSAGE
  83. @rows = []
  84. @columns = []
  85. render_run
  86. else
  87. continue_run
  88. end
  89. elsif @success
  90. @run_id = Blazer.async ? SecureRandom.uuid : nil
  91. options = {user: blazer_user, query: @query, refresh_cache: params[:check], run_id: @run_id}
  92. if Blazer.async && request.format.symbol != :csv
  93. result = []
  94. Blazer::RunStatementJob.perform_async(result, @data_source, @statement, options)
  95. wait_start = Time.now
  96. loop do
  97. sleep(0.02)
  98. break if result.any? || Time.now - wait_start > 3
  99. end
  100. @result = result.first
  101. else
  102. @result = RunStatement.new.perform(@data_source, @statement, options)
  103. end
  104. if @result
  105. @data_source.delete_results(@run_id) if @run_id
  106. @columns = @result.columns
  107. @rows = @result.rows
  108. @error = @result.error
  109. @cached_at = @result.cached_at
  110. @just_cached = @result.just_cached
  111. render_run
  112. else
  113. @timestamp = Time.now.to_i
  114. continue_run
  115. end
  116. else
  117. render layout: false
  118. end
  119. end
  120. def refresh
  121. data_source = Blazer.data_sources[@query.data_source]
  122. @statement = @query.statement.dup
  123. process_vars(@statement, @query.data_source)
  124. Blazer.transform_statement.call(data_source, @statement) if Blazer.transform_statement
  125. data_source.clear_cache(@statement)
  126. redirect_to query_path(@query, variable_params)
  127. end
  128. def update
  129. if params[:commit] == "Fork"
  130. @query = Blazer::Query.new
  131. @query.creator = blazer_user if @query.respond_to?(:creator)
  132. end
  133. unless @query.editable?(blazer_user)
  134. @query.errors.add(:base, "Sorry, permission denied")
  135. end
  136. if @query.errors.empty? && @query.update(query_params)
  137. redirect_to query_path(@query, variable_params)
  138. else
  139. render :edit
  140. end
  141. end
  142. def destroy
  143. @query.destroy if @query.editable?(blazer_user)
  144. redirect_to root_url
  145. end
  146. def tables
  147. @tables = Blazer.data_sources[params[:data_source]].tables
  148. render partial: "tables", layout: false
  149. end
  150. private
  151. def continue_run
  152. render json: {run_id: @run_id, timestamp: @timestamp}, status: :accepted
  153. end
  154. def render_run
  155. @checks = @query ? @query.checks : []
  156. @first_row = @rows.first || []
  157. @column_types = []
  158. if @rows.any?
  159. @columns.each_with_index do |column, i|
  160. @column_types << (
  161. case @first_row[i]
  162. when Integer
  163. "int"
  164. when Float
  165. "float"
  166. else
  167. "string-ins"
  168. end
  169. )
  170. end
  171. end
  172. @filename = @query.name.parameterize if @query
  173. @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)
  174. @boom = @result.boom if @result
  175. @linked_columns = @data_source.linked_columns
  176. @markers = []
  177. [["latitude", "longitude"], ["lat", "lon"]].each do |keys|
  178. lat_index = @columns.index(keys.first)
  179. lon_index = @columns.index(keys.last)
  180. if lat_index && lon_index
  181. @markers =
  182. @rows.select do |r|
  183. r[lat_index] && r[lon_index]
  184. end.map do |r|
  185. {
  186. title: r.each_with_index.map{ |v, i| i == lat_index || i == lon_index ? nil : "<strong>#{@columns[i]}:</strong> #{v}" }.compact.join("<br />").truncate(140),
  187. latitude: r[lat_index],
  188. longitude: r[lon_index]
  189. }
  190. end
  191. end
  192. end
  193. respond_to do |format|
  194. format.html do
  195. render layout: false
  196. end
  197. format.csv do
  198. send_data csv_data(@columns, @rows, @data_source), type: "text/csv; charset=utf-8; header=present", disposition: "attachment; filename=\"#{@query.try(:name).try(:parameterize).presence || 'query'}.csv\""
  199. end
  200. end
  201. end
  202. def set_queries(limit = nil)
  203. @my_queries =
  204. if limit && blazer_user
  205. favorite_query_ids = Blazer::Audit.where(user_id: blazer_user.id).where("created_at > ?", 30.days.ago).where("query_id IS NOT NULL").group(:query_id).order("count_all desc").count.keys
  206. queries = Blazer::Query.named.where(id: favorite_query_ids)
  207. queries = queries.includes(:creator) if Blazer.user_class
  208. queries = queries.index_by(&:id)
  209. favorite_query_ids.map { |query_id| queries[query_id] }.compact
  210. else
  211. []
  212. end
  213. @queries = Blazer::Query.named.order(:name)
  214. @queries = @queries.where("id NOT IN (?)", @my_queries.map(&:id)) if @my_queries.any?
  215. @queries = @queries.includes(:creator) if Blazer.user_class
  216. @queries = @queries.limit(limit) if limit
  217. @queries =
  218. (@my_queries + @queries).map do |q|
  219. {
  220. id: q.id,
  221. name: view_context.link_to(q.name, q),
  222. creator: blazer_user && q.try(:creator) == blazer_user ? "You" : q.try(:creator).try(Blazer.user_name),
  223. hide: q.name.gsub(/\s+/, ""),
  224. vars: extract_vars(q.statement).join(", ")
  225. }
  226. end
  227. end
  228. def set_query
  229. @query = Blazer::Query.find(params[:id].to_s.split("-").first)
  230. end
  231. def query_params
  232. params.require(:query).permit(:name, :description, :statement, :data_source)
  233. end
  234. def blazer_params
  235. params[:blazer] || {}
  236. end
  237. def csv_data(columns, rows, data_source)
  238. CSV.generate do |csv|
  239. csv << columns
  240. rows.each do |row|
  241. csv << row.each_with_index.map { |v, i| v.is_a?(Time) ? blazer_time_value(data_source, columns[i], v) : v }
  242. end
  243. end
  244. end
  245. def blazer_time_value(data_source, k, v)
  246. data_source.local_time_suffix.any? { |s| k.ends_with?(s) } ? v.to_s.sub(" UTC", "") : v.in_time_zone(Blazer.time_zone)
  247. end
  248. helper_method :blazer_time_value
  249. end
  250. end