reports_controller.rb 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
  2. class ReportsController < ApplicationController
  3. prepend_before_action { authentication_check(permission: 'report') }
  4. # GET /api/reports/config
  5. def reporting_config
  6. if !Report.enabled?
  7. render json: {
  8. error: 'Elasticsearch need 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. result = {}
  22. get_params[:metric][:backend].each do |backend|
  23. condition = get_params[:profile].condition
  24. if backend[:condition]
  25. backend[:condition].merge(condition)
  26. else
  27. backend[:condition] = condition
  28. end
  29. next if !backend[:adapter]
  30. result[backend[:name]] = backend[:adapter].aggs(
  31. range_start: get_params[:start],
  32. range_end: get_params[:stop],
  33. interval: get_params[:range],
  34. selector: backend[:condition],
  35. params: backend[:params],
  36. )
  37. end
  38. #created = aggs(start, stop, range, 'created_at', profile.condition)
  39. #closed = aggs(start, stop, range, 'close_at', profile.condition)
  40. #first_solution =
  41. #reopend = backend(start, stop, range, Report::TicketReopened, profile.condition)
  42. # add backlog
  43. #backlogs = []
  44. #position = -1
  45. #created.each {|_not_used|
  46. # position += 1
  47. # diff = created[position][1] - closed[position][1]
  48. # backlog = [position+1, diff]
  49. # backlogs.push backlog
  50. #}
  51. render json: {
  52. data: result
  53. }
  54. end
  55. # GET /api/reports/sets
  56. def sets
  57. get_params = params_all
  58. return if !get_params
  59. if !params[:downloadBackendSelected]
  60. render json: {
  61. error: 'No such downloadBackendSelected param',
  62. }, status: :unprocessable_entity
  63. return
  64. end
  65. # get data
  66. result = {}
  67. get_params[:metric][:backend].each do |backend|
  68. next if params[:downloadBackendSelected] != backend[:name]
  69. condition = get_params[:profile].condition
  70. if backend[:condition]
  71. backend[:condition].merge(condition)
  72. else
  73. backend[:condition] = condition
  74. end
  75. next if !backend[:adapter]
  76. result = backend[:adapter].items(
  77. range_start: get_params[:start],
  78. range_end: get_params[:stop],
  79. interval: get_params[:range],
  80. selector: backend[:condition],
  81. params: backend[:params],
  82. sheet: params[:sheet],
  83. )
  84. result = { count: 0, ticket_ids: [] } if result.nil?
  85. # generate sheet
  86. next if !params[:sheet]
  87. content = sheet(get_params[:profile], backend[:display], result)
  88. send_data(
  89. content,
  90. filename: "tickets-#{get_params[:profile].name}-#{backend[:display]}.xls",
  91. type: 'application/vnd.ms-excel',
  92. disposition: 'attachment'
  93. )
  94. end
  95. return if params[:sheet]
  96. render json: result
  97. end
  98. def params_all
  99. profile = nil
  100. if !params[:profiles] && !params[:profile_id]
  101. raise Exceptions::UnprocessableEntity, 'No such profiles param'
  102. end
  103. if params[:profile_id]
  104. profile = Report::Profile.find(params[:profile_id])
  105. else
  106. params[:profiles].each do |profile_id, active|
  107. next if !active
  108. profile = Report::Profile.find(profile_id)
  109. end
  110. end
  111. if !profile
  112. raise Exceptions::UnprocessableEntity, 'No such active profile'
  113. end
  114. local_config = Report.config
  115. if !local_config || !local_config[:metric] || !local_config[:metric][params[:metric].to_sym]
  116. raise Exceptions::UnprocessableEntity, "No such metric #{params[:metric]}"
  117. end
  118. metric = local_config[:metric][params[:metric].to_sym]
  119. #{"metric"=>"count", "year"=>2015, "month"=>10, "week"=>43, "day"=>20, "timeSlot"=>"year", "report"=>{"metric"=>"count", "year"=>2015, "month"=>10, "week"=>43, "day"=>20, "timeSlot"=>"year"}}
  120. if params[:timeRange] == 'realtime'
  121. start = (Time.zone.now - 60.minutes).iso8601
  122. stop = Time.zone.now.iso8601
  123. range = 'minute'
  124. elsif params[:timeRange] == 'day'
  125. date = Date.parse("#{params[:year]}-#{params[:month]}-#{params[:day]}").to_s
  126. start = "#{date}T00:00:00Z"
  127. stop = "#{date}T23:59:59Z"
  128. range = 'hour'
  129. elsif params[:timeRange] == 'week'
  130. start = Date.commercial(params[:year].to_i, params[:week].to_i).iso8601
  131. stop = Date.parse(start).end_of_week.iso8601
  132. range = 'week'
  133. elsif params[:timeRange] == 'month'
  134. start = Date.parse("#{params[:year]}-#{params[:month]}-01}").iso8601
  135. stop = Date.parse(start).end_of_month.iso8601
  136. range = 'day'
  137. else
  138. start = "#{params[:year]}-01-01"
  139. stop = Date.parse("#{params[:year]}-12-31").iso8601
  140. range = 'month'
  141. end
  142. {
  143. profile: profile,
  144. metric: metric,
  145. config: local_config,
  146. start: start,
  147. stop: stop,
  148. range: range,
  149. }
  150. end
  151. def sheet(profile, title, result)
  152. # Create a new Excel workbook
  153. temp_file = Tempfile.new('time_tracking.xls')
  154. workbook = WriteExcel.new(temp_file)
  155. # Add a worksheet
  156. worksheet = workbook.add_worksheet
  157. worksheet.set_column(0, 0, 10)
  158. worksheet.set_column(1, 1, 34)
  159. worksheet.set_column(2, 2, 10)
  160. worksheet.set_column(3, 3, 10)
  161. worksheet.set_column(4, 8, 20)
  162. # Add and define a format
  163. format = workbook.add_format
  164. format.set_bold
  165. format.set_size(14)
  166. format.set_color('black')
  167. worksheet.set_row(0, 0, 6)
  168. # Write a formatted and unformatted string, row and column notation.
  169. worksheet.write_string(0, 0, "Tickets: #{profile.name} (#{title})", format)
  170. format_header = workbook.add_format
  171. format_header.set_italic
  172. format_header.set_bg_color('gray')
  173. format_header.set_color('white')
  174. worksheet.write_string(2, 0, '#', format_header)
  175. worksheet.write_string(2, 1, 'Title', format_header)
  176. worksheet.write_string(2, 2, 'State', format_header)
  177. worksheet.write_string(2, 3, 'Priority', format_header)
  178. worksheet.write_string(2, 4, 'Group', format_header)
  179. worksheet.write_string(2, 5, 'Owner', format_header)
  180. worksheet.write_string(2, 6, 'Customer', format_header)
  181. worksheet.write_string(2, 7, 'Organization', format_header)
  182. worksheet.write_string(2, 8, 'Create Channel', format_header)
  183. worksheet.write_string(2, 9, 'Sender', format_header)
  184. worksheet.write_string(2, 10, 'Tags', format_header)
  185. worksheet.write_string(2, 11, 'Created at', format_header)
  186. worksheet.write_string(2, 12, 'Updated at', format_header)
  187. worksheet.write_string(2, 13, 'Closed at', format_header)
  188. # ObjectManager attributes
  189. header_column = 14
  190. # needs to be skipped
  191. objects = ObjectManager::Attribute.where(editable: true,
  192. active: true,
  193. object_lookup_id: ObjectLookup.lookup(name: 'Ticket').id)
  194. .pluck(:name, :display, :data_type, :data_option)
  195. .map { |name, display, data_type, data_option| { name: name, display: display, data_type: data_type, data_option: data_option } }
  196. objects.each do |object|
  197. worksheet.write_string(2, header_column, object[:display].capitalize, format_header)
  198. header_column += 1
  199. end
  200. row = 2
  201. result[:ticket_ids].each do |ticket_id|
  202. begin
  203. ticket = Ticket.lookup(id: ticket_id)
  204. row += 1
  205. worksheet.write_string(row, 0, ticket.number)
  206. worksheet.write_string(row, 1, ticket.title)
  207. worksheet.write_string(row, 2, ticket.state.name)
  208. worksheet.write_string(row, 3, ticket.priority.name)
  209. worksheet.write_string(row, 4, ticket.group.name)
  210. worksheet.write_string(row, 5, ticket.owner.fullname)
  211. worksheet.write_string(row, 6, ticket.customer.fullname)
  212. worksheet.write_string(row, 7, ticket.try(:organization).try(:name))
  213. worksheet.write_string(row, 8, ticket.create_article_type.name)
  214. worksheet.write_string(row, 9, ticket.create_article_sender.name)
  215. worksheet.write_string(row, 10, ticket.tag_list.join(','))
  216. worksheet.write_date_time(row, 11, ticket.created_at.to_time.iso8601)
  217. worksheet.write_date_time(row, 12, ticket.updated_at.to_time.iso8601)
  218. worksheet.write_date_time(row, 13, ticket.close_at.to_time.iso8601) if ticket.close_at.present?
  219. # Object Manager attributes
  220. column = 14
  221. # We already queried ObjectManager::Attributes, so we just use objects
  222. objects.each do |object|
  223. key = object[:name]
  224. case object[:data_type]
  225. when 'boolean', 'select'
  226. value = object[:data_option]['options'][ticket.send(key.to_sym)]
  227. worksheet.write_string(row, column, value)
  228. when 'datetime', 'date'
  229. worksheet.write_date_time(row, column, ticket.send(key.to_sym).to_time.iso8601) if ticket.send(key.to_sym).present?
  230. else
  231. # for text, integer and tree select
  232. worksheet.write_string(row, column, ticket.send(key.to_sym))
  233. end
  234. column += 1
  235. end
  236. rescue => e
  237. Rails.logger.error "SKIP: #{e.message}"
  238. end
  239. end
  240. workbook.close
  241. # read file again
  242. file = File.new(temp_file, 'r')
  243. contents = file.read
  244. file.close
  245. contents
  246. end
  247. end