queries_controller.rb 9.8 KB

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