reports_controller.rb 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. # Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
  2. class ReportsController < ApplicationController
  3. prepend_before_action { authentication_check && authorize! }
  4. # GET /api/reports/config
  5. def reporting_config
  6. if !Report.enabled?
  7. render json: {
  8. error: __('Elasticsearch needs to be configured!'),
  9. }
  10. return
  11. end
  12. render json: {
  13. config: Report.config,
  14. profiles: Report::Profile.list,
  15. }
  16. end
  17. # GET /api/reports/generate
  18. def generate
  19. get_params = params_all
  20. return if !get_params
  21. begin
  22. result = {}
  23. get_params[:metric][:backend].each do |backend|
  24. condition = get_params[:profile].condition
  25. if backend[:condition]
  26. backend[:condition].merge(condition)
  27. else
  28. backend[:condition] = condition
  29. end
  30. next if !backend[:adapter]
  31. result[backend[:name]] = backend[:adapter].aggs(
  32. range_start: get_params[:start],
  33. range_end: get_params[:stop],
  34. interval: get_params[:range],
  35. selector: backend[:condition],
  36. params: backend[:params],
  37. timezone: get_params[:timezone],
  38. timezone_offset: get_params[:timezone_offset],
  39. current_user: current_user
  40. )
  41. end
  42. rescue => e
  43. if e.message.include? 'Conflicting date range'
  44. raise Exceptions::UnprocessableEntity, __('Conflicting date ranges. Please check your selected report profile.')
  45. end
  46. raise e
  47. end
  48. render json: {
  49. data: result
  50. }
  51. end
  52. # GET /api/reports/sets
  53. def sets
  54. get_params = params_all
  55. return if !get_params
  56. if !params[:downloadBackendSelected]
  57. render json: {
  58. error: __("Required parameter 'downloadBackendSelected' is missing."),
  59. }, status: :unprocessable_entity
  60. return
  61. end
  62. # get data
  63. result = {}
  64. excel = nil
  65. filename = nil
  66. get_params[:metric][:backend].each do |backend|
  67. next if params[:downloadBackendSelected] != backend[:name]
  68. condition = get_params[:profile].condition
  69. if backend[:condition]
  70. backend[:condition].merge(condition)
  71. else
  72. backend[:condition] = condition
  73. end
  74. next if !backend[:adapter]
  75. result = backend[:adapter].items(
  76. range_start: get_params[:start],
  77. range_end: get_params[:stop],
  78. interval: get_params[:range],
  79. selector: backend[:condition],
  80. params: backend[:params],
  81. sheet: params[:sheet],
  82. timezone: get_params[:timezone],
  83. timezone_offset: get_params[:timezone_offset],
  84. current_user: current_user
  85. )
  86. result = { count: 0, ticket_ids: [] } if result.nil?
  87. # generate sheet
  88. if params[:sheet]
  89. excel = ExcelSheet::Ticket.new(
  90. title: "#{get_params[:profile].name} (#{backend[:display]})",
  91. ticket_ids: result[:ticket_ids],
  92. timezone: params[:timezone],
  93. locale: current_user.locale,
  94. )
  95. filename = "tickets-#{get_params[:profile].name}-#{backend[:display]}.xls"
  96. end
  97. break
  98. end
  99. if excel
  100. send_data(
  101. excel.content,
  102. filename: filename,
  103. type: 'application/vnd.ms-excel',
  104. disposition: 'attachment'
  105. )
  106. return
  107. end
  108. render json: result
  109. end
  110. def params_all
  111. profile = nil
  112. if !params[:profiles] && !params[:profile_id]
  113. raise Exceptions::UnprocessableEntity, __("Required parameter 'profile' is missing.")
  114. end
  115. if params[:profile_id]
  116. profile = Report::Profile.find(params[:profile_id])
  117. else
  118. params[:profiles].each do |profile_id, active|
  119. next if !active
  120. profile = Report::Profile.find(profile_id)
  121. end
  122. end
  123. if !profile
  124. raise Exceptions::UnprocessableEntity, __('The active reporting profile could not be found.')
  125. end
  126. local_config = Report.config
  127. if !local_config || !local_config[:metric] || !local_config[:metric][params[:metric].to_sym]
  128. raise Exceptions::UnprocessableEntity, "Could not find metric #{params[:metric]}"
  129. end
  130. metric = local_config[:metric][params[:metric].to_sym]
  131. case params[:timeRange]
  132. when 'realtime'
  133. start_at = 60.minutes.ago
  134. stop_at = Time.zone.now
  135. range = 'minute'
  136. when 'day'
  137. date = Date.parse("#{params[:year]}-#{params[:month]}-#{params[:day]}").to_s
  138. start_at = Time.zone.parse("#{date}T00:00:00Z")
  139. stop_at = Time.zone.parse("#{date}T23:59:59Z")
  140. range = 'hour'
  141. when 'week'
  142. start_week_at = Date.commercial(params[:year].to_i, params[:week].to_i)
  143. stop_week_at = start_week_at.end_of_week
  144. start_at = Time.zone.parse("#{start_week_at.year}-#{start_week_at.month}-#{start_week_at.day}T00:00:00Z")
  145. stop_at = Time.zone.parse("#{stop_week_at.year}-#{stop_week_at.month}-#{stop_week_at.day}T23:59:59Z")
  146. range = 'week'
  147. when 'month'
  148. start_at = Time.zone.parse("#{params[:year]}-#{params[:month]}-01T00:00:00Z")
  149. stop_at = Time.zone.parse("#{params[:year]}-#{params[:month]}-#{start_at.end_of_month.day}T23:59:59Z")
  150. range = 'day'
  151. else
  152. start_at = Time.zone.parse("#{params[:year]}-01-01T00:00:00Z")
  153. stop_at = Time.zone.parse("#{params[:year]}-12-31T23:59:59Z")
  154. range = 'month'
  155. end
  156. params[:timezone] ||= Setting.get('timezone_default')
  157. if params[:timezone].present? && params[:timeRange] != 'realtime'
  158. offset = stop_at.in_time_zone(params[:timezone]).utc_offset
  159. start_at -= offset
  160. stop_at -= offset
  161. end
  162. {
  163. profile: profile,
  164. metric: metric,
  165. config: local_config,
  166. start: start_at,
  167. stop: stop_at,
  168. range: range,
  169. timezone: params[:timezone],
  170. timezone_offset: offset,
  171. }
  172. end
  173. end