queries_controller.rb 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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. id: d.id,
  16. name: d.name,
  17. creator: blazer_user && d.try(:creator) == blazer_user ? "You" : d.try(:creator).try(Blazer.user_name),
  18. to_param: d.to_param,
  19. dashboard: true
  20. }
  21. end
  22. gon.push(
  23. dashboards: @dashboards,
  24. queries: @queries,
  25. more: @more
  26. )
  27. end
  28. def index
  29. set_queries
  30. render json: @queries
  31. end
  32. def new
  33. @query = Blazer::Query.new(
  34. data_source: params[:data_source],
  35. name: params[:name]
  36. )
  37. if params[:fork_query_id]
  38. @query.statement ||= Blazer::Query.find(params[:fork_query_id]).try(:statement)
  39. end
  40. end
  41. def create
  42. @query = Blazer::Query.new(query_params)
  43. @query.creator = blazer_user if @query.respond_to?(:creator)
  44. if @query.save
  45. redirect_to query_path(@query, variable_params)
  46. else
  47. render_errors @query
  48. end
  49. end
  50. def show
  51. @statement = @query.statement.dup
  52. process_vars(@statement, @query.data_source)
  53. @smart_vars = {}
  54. @sql_errors = []
  55. data_source = Blazer.data_sources[@query.data_source]
  56. @bind_vars.each do |var|
  57. smart_var, error = parse_smart_variables(var, data_source)
  58. @smart_vars[var] = smart_var if smart_var
  59. @sql_errors << error if error
  60. end
  61. Blazer.transform_statement.call(data_source, @statement) if Blazer.transform_statement
  62. end
  63. def edit
  64. end
  65. def run
  66. @statement = params[:statement]
  67. data_source = params[:data_source]
  68. process_vars(@statement, data_source)
  69. @only_chart = params[:only_chart]
  70. @run_id = blazer_params[:run_id]
  71. @query = Query.find_by(id: params[:query_id]) if params[:query_id]
  72. data_source = @query.data_source if @query && @query.data_source
  73. @data_source = Blazer.data_sources[data_source]
  74. if @run_id
  75. @timestamp = blazer_params[:timestamp].to_i
  76. @result = @data_source.run_results(@run_id)
  77. @success = !@result.nil?
  78. if @success
  79. @data_source.delete_results(@run_id)
  80. @columns = @result.columns
  81. @rows = @result.rows
  82. @error = @result.error
  83. @just_cached = !@result.error && @result.cached_at.present?
  84. @cached_at = nil
  85. params[:data_source] = nil
  86. render_run
  87. elsif Time.now > Time.at(@timestamp + (@data_source.timeout || 600).to_i + 5)
  88. # query lost
  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. @tables = Blazer.data_sources[params[:data_source]].tables
  155. render partial: "tables", layout: false
  156. end
  157. def schema
  158. @schema = Blazer.data_sources[params[:data_source]].schema
  159. end
  160. def cancel
  161. Blazer.data_sources[params[:data_source]].cancel(blazer_run_id)
  162. render json: {}
  163. end
  164. private
  165. def continue_run
  166. render json: {run_id: @run_id, timestamp: @timestamp}, status: :accepted
  167. end
  168. def render_run
  169. @checks = @query ? @query.checks.order(:id) : []
  170. @first_row = @rows.first || []
  171. @column_types = []
  172. if @rows.any?
  173. @columns.each_with_index do |column, i|
  174. @column_types << (
  175. case @first_row[i]
  176. when Integer
  177. "int"
  178. when Float, BigDecimal
  179. "float"
  180. else
  181. "string-ins"
  182. end
  183. )
  184. end
  185. end
  186. @filename = @query.name.parameterize if @query
  187. @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)
  188. @boom = @result.boom if @result
  189. @linked_columns = @data_source.linked_columns
  190. @markers = []
  191. [["latitude", "longitude"], ["lat", "lon"], ["lat", "lng"]].each do |keys|
  192. lat_index = @columns.index(keys.first)
  193. lon_index = @columns.index(keys.last)
  194. if lat_index && lon_index
  195. @markers =
  196. @rows.select do |r|
  197. r[lat_index] && r[lon_index]
  198. end.map do |r|
  199. {
  200. 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),
  201. latitude: r[lat_index],
  202. longitude: r[lon_index]
  203. }
  204. end
  205. end
  206. end
  207. respond_to do |format|
  208. format.html do
  209. render layout: false
  210. end
  211. format.csv do
  212. 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\""
  213. end
  214. end
  215. end
  216. def set_queries(limit = nil)
  217. @my_queries =
  218. if limit && blazer_user && !params[:filter] && Blazer.audit
  219. 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)
  220. else
  221. []
  222. end
  223. @queries = Blazer::Query.named.select(:id, :name, :creator_id, :statement)
  224. @queries = @queries.includes(:creator) if Blazer.user_class
  225. if blazer_user && params[:filter] == "mine"
  226. @queries = @queries.where(creator_id: blazer_user.id).reorder(updated_at: :desc)
  227. elsif blazer_user && params[:filter] == "viewed" && Blazer.audit
  228. @queries = queries_by_ids(Blazer::Audit.where(user_id: blazer_user.id).order(created_at: :desc).limit(500).pluck(:query_id).uniq)
  229. else
  230. @queries = @queries.where("id NOT IN (?)", @my_queries.map(&:id)) if @my_queries.any?
  231. @queries = @queries.limit(limit) if limit
  232. @queries = @queries.order(:name)
  233. end
  234. @queries = @queries.to_a
  235. @more = limit && @queries.size >= limit
  236. @queries = (@my_queries + @queries).select { |q| !q.name.to_s.start_with?("#") || q.try(:creator).try(:id) == blazer_user.try(:id) }
  237. @queries =
  238. @queries.map do |q|
  239. {
  240. id: q.id,
  241. name: q.name,
  242. creator: blazer_user && q.try(:creator) == blazer_user ? "You" : q.try(:creator).try(Blazer.user_name),
  243. vars: extract_vars(q.statement).join(", "),
  244. to_param: q.to_param
  245. }
  246. end
  247. end
  248. def queries_by_ids(favorite_query_ids)
  249. queries = Blazer::Query.named.where(id: favorite_query_ids)
  250. queries = queries.includes(:creator) if Blazer.user_class
  251. queries = queries.index_by(&:id)
  252. favorite_query_ids.map { |query_id| queries[query_id] }.compact
  253. end
  254. def set_query
  255. @query = Blazer::Query.find(params[:id].to_s.split("-").first)
  256. end
  257. def query_params
  258. params.require(:query).permit(:name, :description, :statement, :data_source)
  259. end
  260. def blazer_params
  261. params[:blazer] || {}
  262. end
  263. def csv_data(columns, rows, data_source)
  264. CSV.generate do |csv|
  265. csv << columns
  266. rows.each do |row|
  267. csv << row.each_with_index.map { |v, i| v.is_a?(Time) ? blazer_time_value(data_source, columns[i], v) : v }
  268. end
  269. end
  270. end
  271. def blazer_time_value(data_source, k, v)
  272. data_source.local_time_suffix.any? { |s| k.ends_with?(s) } ? v.to_s.sub(" UTC", "") : v.in_time_zone(Blazer.time_zone)
  273. end
  274. helper_method :blazer_time_value
  275. def blazer_run_id
  276. params[:run_id].to_s.gsub(/[^a-z0-9\-]/i, "")
  277. end
  278. end
  279. end