queries_controller.rb 9.7 KB

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