base.rb 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class Report::Base
  3. # :object
  4. # :type created|updated
  5. # :attribute
  6. # :value_from
  7. # :value_to
  8. # :start
  9. # :end
  10. # :selector
  11. def self.history_count(params)
  12. history_object = History::Object.lookup(name: params[:object])
  13. query, bind_params, tables = Ticket.selector2sql(params[:selector])
  14. # created
  15. if params[:type] == 'created'
  16. history_type = History::Type.lookup(name: 'created')
  17. return History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
  18. .where(
  19. 'histories.created_at >= ? AND histories.created_at <= ? AND histories.history_object_id = ? AND histories.history_type_id = ?', params[:start], params[:end], history_object.id, history_type.id
  20. )
  21. .where(query, *bind_params).joins(tables).count
  22. end
  23. # updated
  24. if params[:type] == 'updated'
  25. history_type = History::Type.lookup(name: 'updated')
  26. history_attribute = History::Attribute.lookup(name: params[:attribute])
  27. result = nil
  28. if !history_attribute || !history_type
  29. result = 0
  30. elsif params[:id_not_from] && params[:id_to]
  31. result = History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
  32. .where(query, *bind_params).joins(tables)
  33. .where(
  34. 'histories.created_at >= ? AND histories.created_at <= ? AND histories.history_object_id = ? AND histories.history_type_id = ? AND histories.history_attribute_id IN (?) AND histories.id_from NOT IN (?) AND histories.id_to IN (?)',
  35. params[:start],
  36. params[:end],
  37. history_object.id,
  38. history_type.id,
  39. history_attribute.id,
  40. params[:id_not_from],
  41. params[:id_to],
  42. ).count
  43. elsif params[:id_from] && params[:id_not_to]
  44. result = History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
  45. .where(query, *bind_params).joins(tables)
  46. .where(
  47. 'histories.created_at >= ? AND histories.created_at <= ? AND histories.history_object_id = ? AND histories.history_type_id = ? AND histories.history_attribute_id IN (?) AND histories.id_from IN (?) AND histories.id_to NOT IN (?)',
  48. params[:start],
  49. params[:end],
  50. history_object.id,
  51. history_type.id,
  52. history_attribute.id,
  53. params[:id_from],
  54. params[:id_not_to],
  55. ).count
  56. elsif params[:value_from] && params[:value_not_to]
  57. result = History.joins('INNER JOIN tickets ON tickets.id = histories.o_id')
  58. .where(query, *bind_params).joins(tables)
  59. .where(
  60. 'histories.created_at >= ? AND histories.created_at <= ? AND histories.history_object_id = ? AND histories.history_type_id = ? AND histories.history_attribute_id IN (?) AND histories.value_from IN (?) AND histories.value_to NOT IN (?)',
  61. params[:start],
  62. params[:end],
  63. history_object.id,
  64. history_type.id,
  65. history_attribute.id,
  66. params[:value_from],
  67. params[:value_not_to],
  68. ).count
  69. elsif params[:value_to]
  70. result = History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
  71. .where(query, *bind_params).joins(tables)
  72. .where(
  73. 'histories.created_at >= ? AND histories.created_at <= ? AND histories.history_object_id = ? AND histories.history_type_id = ? AND histories.history_attribute_id IN (?) AND histories.value_to IN (?)',
  74. params[:start],
  75. params[:end],
  76. history_object.id,
  77. history_type.id,
  78. history_attribute.id,
  79. params[:value_to],
  80. ).count
  81. elsif params[:id_to]
  82. result = History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
  83. .where(query, *bind_params).joins(tables)
  84. .where(
  85. 'histories.created_at >= ? AND histories.created_at <= ? AND histories.history_object_id = ? AND histories.history_type_id = ? AND histories.history_attribute_id IN (?) AND histories.id_to IN (?)',
  86. params[:start],
  87. params[:end],
  88. history_object.id,
  89. history_type.id,
  90. history_attribute.id,
  91. params[:id_to],
  92. ).count
  93. end
  94. return result if !result.nil?
  95. raise "UNKOWN params (#{params.inspect})!"
  96. end
  97. raise "UNKOWN :type (#{params[:type]})!"
  98. end
  99. # :object
  100. # :type created|updated
  101. # :attribute
  102. # :value_from
  103. # :value_to
  104. # :start
  105. # :end
  106. # :condition
  107. def self.history(data)
  108. history_object = History::Object.lookup(name: data[:object])
  109. query, bind_params, tables = Ticket.selector2sql(data[:selector])
  110. count = 0
  111. ticket_ids = []
  112. # created
  113. if data[:type] == 'created'
  114. history_type = History::Type.lookup(name: 'created')
  115. histories = History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
  116. .where(
  117. 'histories.created_at >= ? AND histories.created_at <= ? AND histories.history_object_id = ? AND histories.history_type_id = ?', data[:start], data[:end], history_object.id, history_type.id
  118. )
  119. .where(query, *bind_params).joins(tables)
  120. histories.each do |history|
  121. count += 1
  122. ticket_ids.push history.o_id
  123. end
  124. return {
  125. count: count,
  126. ticket_ids: ticket_ids,
  127. }
  128. end
  129. # updated
  130. if data[:type] == 'updated'
  131. history_type = History::Type.lookup(name: 'updated')
  132. history_attribute = History::Attribute.lookup(name: data[:attribute])
  133. if !history_attribute || !history_type
  134. count = 0
  135. else
  136. if data[:id_not_from] && data[:id_to]
  137. histories = History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
  138. .where(query, *bind_params).joins(tables)
  139. .where(
  140. 'histories.created_at >= ? AND histories.created_at <= ? AND histories.history_object_id = ? AND histories.history_type_id = ? AND histories.history_attribute_id IN (?) AND histories.id_from NOT IN (?) AND histories.id_to IN (?)',
  141. data[:start],
  142. data[:end],
  143. history_object.id,
  144. history_type.id,
  145. history_attribute.id,
  146. data[:id_not_from],
  147. data[:id_to],
  148. )
  149. elsif data[:id_from] && data[:id_not_to]
  150. histories = History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
  151. .where(query, *bind_params).joins(tables)
  152. .where(
  153. 'histories.created_at >= ? AND histories.created_at <= ? AND histories.history_object_id = ? AND histories.history_type_id = ? AND histories.history_attribute_id IN (?) AND histories.id_from IN (?) AND histories.id_to NOT IN (?)',
  154. data[:start],
  155. data[:end],
  156. history_object.id,
  157. history_type.id,
  158. history_attribute.id,
  159. data[:id_from],
  160. data[:id_not_to],
  161. )
  162. elsif data[:value_from] && data[:value_not_to]
  163. histories = History.joins('INNER JOIN tickets ON tickets.id = histories.o_id')
  164. .where(query, *bind_params).joins(tables)
  165. .where(
  166. 'histories.created_at >= ? AND histories.created_at <= ? AND histories.history_object_id = ? AND histories.history_type_id = ? AND histories.history_attribute_id IN (?) AND histories.value_from IN (?) AND histories.value_to NOT IN (?)',
  167. data[:start],
  168. data[:end],
  169. history_object.id,
  170. history_type.id,
  171. history_attribute.id,
  172. data[:value_from],
  173. data[:value_not_to],
  174. )
  175. elsif data[:value_to]
  176. histories = History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
  177. .where(query, *bind_params).joins(tables)
  178. .where(
  179. 'histories.created_at >= ? AND histories.created_at <= ? AND histories.history_object_id = ? AND histories.history_type_id = ? AND histories.history_attribute_id IN (?) AND histories.value_to IN (?)',
  180. data[:start],
  181. data[:end],
  182. history_object.id,
  183. history_type.id,
  184. history_attribute.id,
  185. data[:value_to],
  186. )
  187. elsif data[:id_to]
  188. histories = History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
  189. .where(query, *bind_params).joins(tables)
  190. .where(
  191. 'histories.created_at >= ? AND histories.created_at <= ? AND histories.history_object_id = ? AND histories.history_type_id = ? AND histories.history_attribute_id IN (?) AND histories.id_to IN (?)',
  192. data[:start],
  193. data[:end],
  194. history_object.id,
  195. history_type.id,
  196. history_attribute.id,
  197. data[:id_to],
  198. )
  199. end
  200. histories.each do |history|
  201. count += 1
  202. ticket_ids.push history.o_id
  203. end
  204. end
  205. return {
  206. count: count,
  207. ticket_ids: ticket_ids,
  208. }
  209. end
  210. raise "UNKOWN :type (#{data[:type]})!"
  211. end
  212. # :type
  213. # :start
  214. # :end
  215. # :condition
  216. def self.time_average(data)
  217. query, bind_params, tables = Ticket.selector2sql(data[:condition])
  218. tickets = 0
  219. time_total = 0
  220. Ticket
  221. .where(tickets: { created_at: data[:start]..data[:end] })
  222. .where(query, *bind_params)
  223. .joins(tables)
  224. .each do |ticket|
  225. timestamp = ticket[ data[:type].to_sym ]
  226. next if !timestamp
  227. # puts 'FR:' + first_response.to_s
  228. # puts 'CT:' + ticket.created_at.to_s
  229. diff = timestamp - ticket.created_at
  230. # puts 'DIFF:' + diff.to_s
  231. time_total += diff
  232. tickets += 1
  233. end
  234. if time_total.zero? || tickets.zero?
  235. tickets = -0.001
  236. else
  237. tickets = time_total / tickets / 60
  238. tickets = tickets.to_i
  239. end
  240. {
  241. count: tickets,
  242. }
  243. end
  244. # :type
  245. # :start
  246. # :end
  247. # :condition
  248. def self.time_min(data)
  249. query, bind_params, tables = Ticket.selector2sql(data[:condition])
  250. time_min = 0
  251. ticket_ids = []
  252. Ticket
  253. .where(tickets: { created_at: data[:start]..data[:end] })
  254. .where(query, *bind_params)
  255. .joins(tables)
  256. .each do |ticket|
  257. timestamp = ticket[ data[:type].to_sym ]
  258. next if !timestamp
  259. ticket_ids.push ticket.id
  260. # puts 'FR:' + first_response.to_s
  261. # puts 'CT:' + ticket.created_at.to_s
  262. diff = timestamp - ticket.created_at
  263. # puts 'DIFF:' + diff.to_s
  264. if !time_min
  265. time_min = diff
  266. end
  267. if diff < time_min
  268. time_min = diff
  269. end
  270. end
  271. tickets = if time_min.zero?
  272. -0.001
  273. else
  274. (time_min / 60).to_i
  275. end
  276. {
  277. count: tickets,
  278. ticket_ids: ticket_ids,
  279. }
  280. end
  281. # :type
  282. # :start
  283. # :end
  284. # :condition
  285. def self.time_max(data)
  286. query, bind_params, tables = Ticket.selector2sql(data[:condition])
  287. time_max = 0
  288. ticket_ids = []
  289. Ticket
  290. .where(tickets: { created_at: data[:start]..data[:end] })
  291. .where(query, *bind_params)
  292. .joins(tables)
  293. .each do |ticket|
  294. timestamp = ticket[ data[:type].to_sym ]
  295. next if !timestamp
  296. ticket_ids.push ticket.id
  297. # puts "#{data[:type].to_s} - #{timestamp} - #{ticket.inspect}"
  298. # puts 'FR:' + ticket.first_response.to_s
  299. # puts 'CT:' + ticket.created_at.to_s
  300. diff = timestamp - ticket.created_at
  301. # puts 'DIFF:' + diff.to_s
  302. if !time_max
  303. time_max = diff
  304. end
  305. if diff > time_max
  306. time_max = diff
  307. end
  308. end
  309. tickets = if time_max.zero?
  310. -0.001
  311. else
  312. (time_max / 60).to_i
  313. end
  314. {
  315. count: tickets,
  316. ticket_ids: ticket_ids,
  317. }
  318. end
  319. def self.ticket_condition(ticket_id, condition)
  320. ticket = Ticket.lookup(id: ticket_id)
  321. condition.each do |key, value|
  322. if ticket[key.to_sym] != value
  323. return false
  324. end
  325. end
  326. true
  327. end
  328. INTERVAL_LENGTH = {
  329. month: 12,
  330. week: 7,
  331. # day: 31, Day is counted bellow by given month/year
  332. hour: 24,
  333. minute: 60,
  334. }.with_indifferent_access
  335. def self.interval_length(params)
  336. interval = params[:interval]
  337. if interval == 'day'
  338. return Time.days_in_month params[:range_start].month, params[:range_start].year
  339. end
  340. INTERVAL_LENGTH[interval]
  341. end
  342. end