queries_controller.rb 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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 =
  7. Blazer::Dashboard.order(:name).map do |d|
  8. {
  9. name: "<strong>#{view_context.link_to(d.name, d)}</strong>",
  10. creator: blazer_user && d.try(:creator) == blazer_user ? "You" : d.try(:creator).try(Blazer.user_name),
  11. hide: d.name.gsub(/\s+/, ""),
  12. vars: nil
  13. }
  14. end
  15. end
  16. def index
  17. set_queries
  18. render json: @queries
  19. end
  20. def new
  21. @query = Blazer::Query.new(
  22. data_source: params[:data_source],
  23. name: params[:name]
  24. )
  25. if params[:fork_query_id]
  26. @query.statement ||= Blazer::Query.find(params[:fork_query_id]).try(:statement)
  27. end
  28. end
  29. def create
  30. @query = Blazer::Query.new(query_params)
  31. @query.creator = blazer_user if @query.respond_to?(:creator)
  32. if @query.save
  33. redirect_to query_path(@query, variable_params)
  34. else
  35. render :new
  36. end
  37. end
  38. def show
  39. @statement = @query.statement.dup
  40. process_vars(@statement, @query.data_source)
  41. @smart_vars = {}
  42. @sql_errors = []
  43. data_source = Blazer.data_sources[@query.data_source]
  44. @bind_vars.each do |var|
  45. query = data_source.smart_variables[var]
  46. if query
  47. columns, rows, error, cached_at = data_source.run_statement(query)
  48. @smart_vars[var] = rows.map { |v| v.reverse }
  49. @sql_errors << error if error
  50. end
  51. end
  52. Blazer.transform_statement.call(data_source, @statement) if Blazer.transform_statement
  53. end
  54. def edit
  55. end
  56. def run
  57. @statement = params[:statement]
  58. data_source = params[:data_source]
  59. process_vars(@statement, data_source)
  60. @only_chart = params[:only_chart]
  61. if @success
  62. @query = Query.find_by(id: params[:query_id]) if params[:query_id]
  63. data_source = @query.data_source if @query && @query.data_source
  64. @data_source = Blazer.data_sources[data_source]
  65. @columns, @rows, @error, @cached_at, @just_cached = @data_source.run_main_statement(@statement, user: blazer_user, query: @query, refresh_cache: params[:check])
  66. render_run
  67. end
  68. respond_to do |format|
  69. format.html do
  70. render layout: false
  71. end
  72. format.csv do
  73. 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\""
  74. end
  75. end
  76. end
  77. def refresh
  78. data_source = Blazer.data_sources[@query.data_source]
  79. @statement = @query.statement.dup
  80. process_vars(@statement, @query.data_source)
  81. Blazer.transform_statement.call(data_source, @statement) if Blazer.transform_statement
  82. data_source.clear_cache(@statement)
  83. redirect_to query_path(@query, variable_params)
  84. end
  85. def update
  86. if params[:commit] == "Fork"
  87. @query = Blazer::Query.new
  88. @query.creator = blazer_user if @query.respond_to?(:creator)
  89. end
  90. unless @query.editable?(blazer_user)
  91. @query.errors.add(:base, "Sorry, permission denied")
  92. end
  93. if @query.errors.empty? && @query.update(query_params)
  94. redirect_to query_path(@query, variable_params)
  95. else
  96. render :edit
  97. end
  98. end
  99. def destroy
  100. @query.destroy if @query.editable?(blazer_user)
  101. redirect_to root_url
  102. end
  103. def tables
  104. @tables = Blazer.data_sources[params[:data_source]].tables
  105. render partial: "tables", layout: false
  106. end
  107. private
  108. def render_run
  109. @first_row = @rows.first || []
  110. @column_types = []
  111. if @rows.any?
  112. @columns.each_with_index do |column, i|
  113. @column_types << (
  114. case @first_row[i]
  115. when Integer
  116. "int"
  117. when Float
  118. "float"
  119. else
  120. "string-ins"
  121. end
  122. )
  123. end
  124. end
  125. @filename = @query.name.parameterize if @query
  126. @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] }
  127. @boom = {}
  128. @columns.each_with_index do |key, i|
  129. query = @data_source.smart_columns[key]
  130. if query
  131. values = @rows.map { |r| r[i] }.compact.uniq
  132. columns, rows, error, cached_at = @data_source.run_statement(ActiveRecord::Base.send(:sanitize_sql_array, [query.sub("{value}", "(?)"), values]))
  133. @boom[key] = Hash[rows.map { |k, v| [k.to_s, v] }]
  134. end
  135. end
  136. @linked_columns = @data_source.linked_columns
  137. @markers = []
  138. [["latitude", "longitude"], ["lat", "lon"]].each do |keys|
  139. lat_index = @columns.index(keys.first)
  140. lon_index = @columns.index(keys.last)
  141. if lat_index && lon_index
  142. @markers =
  143. @rows.select do |r|
  144. r[lat_index] && r[lon_index]
  145. end.map do |r|
  146. {
  147. 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),
  148. latitude: r[lat_index],
  149. longitude: r[lon_index]
  150. }
  151. end
  152. end
  153. end
  154. end
  155. def set_queries(limit = nil)
  156. @my_queries =
  157. if limit && blazer_user
  158. 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
  159. queries = Blazer::Query.named.where(id: favorite_query_ids)
  160. queries = queries.includes(:creator) if Blazer.user_class
  161. queries = queries.index_by(&:id)
  162. favorite_query_ids.map { |query_id| queries[query_id] }.compact
  163. else
  164. []
  165. end
  166. @queries = Blazer::Query.named.order(:name)
  167. @queries = @queries.where("id NOT IN (?)", @my_queries.map(&:id)) if @my_queries.any?
  168. @queries = @queries.includes(:creator) if Blazer.user_class
  169. @queries = @queries.limit(limit) if limit
  170. @queries =
  171. (@my_queries + @queries).map do |q|
  172. {
  173. id: q.id,
  174. name: view_context.link_to(q.name, q),
  175. creator: blazer_user && q.try(:creator) == blazer_user ? "You" : q.try(:creator).try(Blazer.user_name),
  176. hide: q.name.gsub(/\s+/, ""),
  177. vars: extract_vars(q.statement).join(", ")
  178. }
  179. end
  180. end
  181. def set_query
  182. @query = Blazer::Query.find(params[:id].to_s.split("-").first)
  183. end
  184. def query_params
  185. params.require(:query).permit(:name, :description, :statement, :data_source)
  186. end
  187. def csv_data(columns, rows, data_source)
  188. CSV.generate do |csv|
  189. csv << columns
  190. rows.each do |row|
  191. csv << row.each_with_index.map { |v, i| v.is_a?(Time) ? blazer_time_value(data_source, columns[i], v) : v }
  192. end
  193. end
  194. end
  195. def blazer_time_value(data_source, k, v)
  196. data_source.local_time_suffix.any? { |s| k.ends_with?(s) } ? v.to_s.sub(" UTC", "") : v.in_time_zone(Blazer.time_zone)
  197. end
  198. helper_method :blazer_time_value
  199. end
  200. end