Next features for mini reporting.

This commit is contained in:
Martin Edenhofer 2015-10-29 03:33:36 +01:00
parent eaa266ec50
commit b9a5de081e
14 changed files with 2042 additions and 233 deletions

View file

@ -87,8 +87,6 @@ class Graph extends App.ControllerContent
constructor: ->
super
return
# rerender view
@bind 'ui:report:rerender', =>
@render()
@ -98,7 +96,14 @@ class Graph extends App.ControllerContent
render: =>
update = (data) =>
@draw(data.data)
# show only selected lines
dataNew = {}
for key, value of data.data
if @params.backendSelected[key] is true
dataNew[key] = value
@draw(dataNew)
t = new Date
@el.find('#download-chart').html(t.toString())
new Download(
@ -109,19 +114,20 @@ class Graph extends App.ControllerContent
)
url = @apiPath + '/reports/generate'
interval = 60000
interval = 5 * 60000
if @params.timeRange is 'year'
interval = 30000
interval = 5 * 60000
if @params.timeRange is 'month'
interval = 20000
interval = 60000
if @params.timeRange is 'week'
interval = 20000
interval = 40000
if @params.timeRange is 'day'
interval = 20000
if @params.timeRange is 'realtime'
interval = 10000
@ajax(
id: 'report_graph'
type: 'POST'
url: url
data: JSON.stringify(
@ -137,7 +143,7 @@ class Graph extends App.ControllerContent
processData: true
success: (data) =>
update(data)
@delay( @render, interval, 'report-update', 'page' )
@delay(@render, interval, 'report-update', 'page')
)
draw: (data) =>
@ -162,10 +168,22 @@ class Graph extends App.ControllerContent
dataPlot = []
for key, value of data
realname = key
if @config.metric[@params.metric]
for backend in @config.metric[@params.metric].backend
if backend.name is key
realname = backend.display
content = []
count = 0
for i in xaxis
content.push [count, value[count]]
count += 1
dataPlot.push {
data: value
label: key
data: content
label: App.i18n.translateInline(realname)
}
# plot
$.plot( $('#placeholder'), dataPlot, {
yaxis: { min: 0 },
@ -190,12 +208,10 @@ class Download extends App.Controller
reports = []
# select first backend, if no backend is selected
@backendSelected = undefined
if @config.metric[@params.metric]
for backend in @config.metric[@params.metric].backend
console.log('bac', backend)
if backend.dataDownload && !@backendSelected
@backendSelected = backend.name
if backend.dataDownload && !@params.downloadBackendSelected
@params.downloadBackendSelected = backend.name
# get used profiles
profiles = []
@ -208,7 +224,7 @@ class Download extends App.Controller
@html App.view('report/download_header')(
reports: reports
profiles: profiles
backendSelected: @backendSelected
downloadBackendSelected: @params.downloadBackendSelected
metric: @config.metric[@params.metric]
)
@ -220,7 +236,7 @@ class Download extends App.Controller
@el.find('.js-dataDownloadBackendSelector').parent().removeClass('active')
$(e.target).parent().addClass('active')
@profileSelectedId = $(e.target).data('profile-id')
@backendSelected = $(e.target).data('backend')
@params.downloadBackendSelected = $(e.target).data('backend')
table = (tickets, count) =>
url = '#ticket/zoom/'
@ -239,6 +255,7 @@ class Download extends App.Controller
@startLoading()
@ajax(
id: 'report_download'
type: 'POST'
url: @apiPath + '/reports/sets'
data: JSON.stringify(
@ -248,8 +265,9 @@ class Download extends App.Controller
week: @params.week
day: @params.day
timeRange: @params.timeRange
profile_id: @profileSelectedId
backend: @backendSelected
profiles: @params.profileSelected
backends: @params.backendSelected
downloadBackendSelected: @params.downloadBackendSelected
)
processData: true
success: (data) =>
@ -459,11 +477,10 @@ class Sidebar extends App.Controller
selectProfile: (e) =>
profile_id = $(e.target).val()
active = $(e.target).prop('checked')
if active
console.log('llll', profile_id)
for key, value of @params.profileSelected
delete @params.profileSelected[key]
@params.profileSelected[profile_id] = true
else
delete @params.profileSelected[profile_id]
App.Event.trigger( 'ui:report:rerender' )
selectBackend: (e) =>

View file

@ -4,7 +4,7 @@
<% for profile in @profiles: %>
<% for backend in @metric.backend: %>
<% if backend.dataDownload: %>
<li <% if backend.name is @backendSelected: %>class="is-active active"<% end %>><a href="#" class="js-dataDownloadBackendSelector" data-toggle="tab" data-profile-id="<%= profile.id %>" data-backend="<%= backend.name %>"><%= @T(backend.display) %></a></li>
<li <% if backend.name is @downloadBackendSelected: %>class="is-active active"<% end %>><a href="#" class="js-dataDownloadBackendSelector" data-toggle="tab" data-profile-id="<%= profile.id %>" data-backend="<%= backend.name %>"><%= @T(backend.display) %></a></li>
<% end %>
<% end %>
<% end %>

View file

@ -1,5 +1,5 @@
<i><%- @T('%s records', @count) %></i>
<a href="<%- @download %>" target="_blank" data-type="attachment" id="downloadsetascsv"><%- @Icon('download') %></a>
<a href="<%- @download %>" target="_blank" data-type="attachment"><%- @Icon('download') %></a>
<table class="table table-striped table-hover">
<thead>
<tr>

View file

@ -1,6 +1,6 @@
<div class="main flex">
<div class="page-header page-header--center">
<div class="page-header">
<div class="page-header-title">
<h1><%- @T( 'Reporting' ) %> <small></small></h1>
</div>
@ -15,14 +15,6 @@
<div id="placeholder" class="" style="height:350px;"></div>
<span class=" muted" id="download-chart" style="font-size: 8px;"></span>
<!--
<a href="<%-@download%>" target="_blank" data-type="attachment" class="pull-right" id="download-chart">
<i class="icon-download" title="<%- @Ti('Download') %>"></i>
</a>
-->
<!--
<div id="overview" style="margin-left:50px;margin-top:20px;width:400px;height:50px"></div>
-->
<div class="js-timePicker"></div>
<div class="js-dataDownload"></div>

View file

@ -31,7 +31,7 @@
<% for profile in @profiles: %>
<li>
<label class="inline-label radio-replacement">
<input class="js-profileSelector" type="radio" value="<%= profile.id %>"<%- ' checked' if @params.profileSelected[profile.id] %>>
<input class="js-profileSelector" type="radio" name="profile_id" value="<%= profile.id %>"<%- ' checked' if @params.profileSelected[profile.id] %>>
<%- @Icon('radio', 'icon-unchecked') %>
<%- @Icon('radio-checked', 'icon-checked') %>
<span class="label-text"><%= profile.name %></span>

View file

@ -16,39 +16,44 @@ class ReportsController < ApplicationController
def generate
return if deny_if_not_role('Report')
#{"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"}}
if params[:timeRange] == 'realtime'
start = (Time.zone.now - 60.minutes).iso8601
stop = Time.zone.now.iso8601
created = aggs(start, stop, 'minute', 'created_at')
closed = aggs(start, stop, 'minute', 'close_time')
elsif params[:timeRange] == 'day'
date = Date.parse("#{params[:year]}-#{params[:month]}-#{params[:day]}").to_s
start = "#{date}T00:00:00Z"
stop = "#{date}T23:59:59Z"
created = aggs(start, stop, 'hour', 'created_at')
closed = aggs(start, stop, 'hour', 'close_time')
elsif params[:timeRange] == 'week'
start = Date.commercial(params[:year], params[:week]).iso8601
stop = Date.parse(start).end_of_week
created = aggs(start, stop, 'week', 'created_at')
closed = aggs(start, stop, 'week', 'close_time')
elsif params[:timeRange] == 'month'
start = Date.parse("#{params[:year]}-#{params[:month]}-01}").iso8601
stop = Date.parse(start).end_of_month
created = aggs(start, stop, 'day', 'created_at')
closed = aggs(start, stop, 'day', 'close_time')
get_params = params_all
return if !get_params
result = {}
get_params[:metric][:backend].each {|backend|
condition = get_params[:profile].condition
if backend[:condition]
backend[:condition].merge(condition)
else
start = "#{params[:year]}-01-01"
stop = "#{params[:year]}-12-31"
created = aggs(start, stop, 'month', 'created_at')
closed = aggs(start, stop, 'month', 'close_time')
backend[:condition] = condition
end
render json: {
data: {
created: created,
closed: closed,
next if !backend[:adapter]
result[backend[:name]] = backend[:adapter].aggs(
range_start: get_params[:start],
range_end: get_params[:stop],
interval: get_params[:range],
selector: backend[:condition],
params: backend[:params],
)
}
#created = aggs(start, stop, range, 'created_at', profile.condition)
#closed = aggs(start, stop, range, 'close_time', profile.condition)
#first_solution =
#reopend = backend(start, stop, range, Report::TicketReopened, profile.condition)
# add backlog
#backlogs = []
#position = -1
#created.each {|_not_used|
# position += 1
# diff = created[position][1] - closed[position][1]
# backlog = [position+1, diff]
# backlogs.push backlog
#}
render json: {
data: result
}
end
@ -56,109 +61,97 @@ class ReportsController < ApplicationController
def sets
return if deny_if_not_role('Report')
get_params = params_all
return if !get_params
if !params[:downloadBackendSelected]
render json: {
error: 'No such downloadBackendSelected param',
}, status: :unprocessable_entity
return
end
# get data
result = {}
get_params[:metric][:backend].each {|backend|
next if params[:downloadBackendSelected] != backend[:name]
condition = get_params[:profile].condition
if backend[:condition]
backend[:condition].merge(condition)
else
backend[:condition] = condition
end
next if !backend[:adapter]
result = backend[:adapter].items(
range_start: get_params[:start],
range_end: get_params[:stop],
interval: get_params[:range],
selector: backend[:condition],
params: backend[:params],
)
}
render json: result
end
def params_all
profile = nil
if !params[:profiles]
render json: {
error: 'No such profiles param',
}, status: :unprocessable_entity
return
end
params[:profiles].each {|profile_id, active|
next if !active
profile = Report::Profile.find(profile_id)
}
if !profile
render json: {
error: 'No such active profile',
}, status: :unprocessable_entity
return
end
config = Report.config
if !config || !config[:metric] || !config[:metric][params[:metric].to_sym]
render json: {
error: "No such metric #{params[:metric]}"
}, status: :unprocessable_entity
return
end
metric = config[:metric][params[:metric].to_sym]
#{"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"}}
if params[:timeRange] == 'realtime'
start = (Time.zone.now - 60.minutes).iso8601
stop = Time.zone.now.iso8601
range = 'minute'
elsif params[:timeRange] == 'day'
date = Date.parse("#{params[:year]}-#{params[:month]}-#{params[:day]}").to_s
start = "#{date}T00:00:00Z"
stop = "#{date}T23:59:59Z"
range = 'hour'
elsif params[:timeRange] == 'week'
start = Date.commercial(params[:year], params[:week]).iso8601
stop = Date.parse(start).end_of_week
range = 'week'
elsif params[:timeRange] == 'month'
start = Date.parse("#{params[:year]}-#{params[:month]}-01}").iso8601
stop = Date.parse(start).end_of_month
range = 'day'
else
start = "#{params[:year]}-01-01"
stop = "#{params[:year]}-12-31"
range = 'month'
end
# get data
ticket_ids = []
assets = {}
Ticket.select('id').all.each {|ticket_part|
ticket = Ticket.lookup(id: ticket_part.id)
assets = ticket.assets(assets)
ticket_ids.push ticket_part.id
}
count = Ticket.count
render json: {
ticket_ids: ticket_ids,
assets: assets,
count: count,
}
end
def aggs(range_start, range_end, interval, field)
interval_es = interval
if interval == 'week'
interval_es = 'day'
end
result = SearchIndexBackend.aggs(
{
},
[range_start, range_end, field, interval_es],
['Ticket'],
)
data = []
if interval == 'month'
start = Date.parse(range_start)
stop_interval = 12
elsif interval == 'week'
start = Date.parse(range_start)
stop_interval = 7
elsif interval == 'day'
start = Date.parse(range_start)
stop_interval = 31
elsif interval == 'hour'
start = Time.zone.parse(range_start)
stop_interval = 24
elsif interval == 'minute'
start = Time.zone.parse(range_start)
stop_interval = 60
end
(1..stop_interval).each {|counter|
match = false
result['aggregations']['time_buckets']['buckets'].each {|item|
if interval == 'minute'
item['key_as_string'] = item['key_as_string'].sub(/:\d\d.\d\d\dZ$/, '')
start_string = start.iso8601.sub(/:\d\dZ$/, '')
else
start_string = start.iso8601.sub(/:\d\d:\d\d.+?$/, '')
end
next if !item['doc_count']
next if item['key_as_string'] !~ /#{start_string}/
next if match
match = true
data.push [counter, item['doc_count']]
if interval == 'month'
start = start.next_month
elsif interval == 'week'
start = start.next_day
elsif interval == 'day'
start = start.next_day
elsif interval == 'hour'
start = start + 1.hour
elsif interval == 'minute'
start = start + 1.minute
end
profile: profile,
metric: metric,
config: config,
start: start,
stop: stop,
range: range,
}
next if match
data.push [counter, 0]
if interval == 'month'
start = start.next_month
elsif interval == 'week'
start = start.next_day
elsif interval == 'day'
start = start + 1.day
elsif interval == 'hour'
start = start + 1.hour
elsif interval == 'minute'
start = start + 1.minute
end
}
data
end
end

View file

@ -16,12 +16,16 @@ class Report
display: 'Created',
selected: true,
dataDownload: true,
adapter: Report::TicketGenericTime,
params: { field: 'created_at' },
},
{
name: 'closed',
display: 'Closed',
selected: true,
dataDownload: true,
adapter: Report::TicketGenericTime,
params: { field: 'close_time' },
},
{
name: 'backlog',
@ -32,39 +36,45 @@ class Report
{
name: 'first_solution',
display: 'First Solution',
selected: true,
selected: false,
dataDownload: true,
adapter: Report::TicketFirstSolution,
},
{
name: 'reopen',
name: 'reopened',
display: 'Re-Open',
selected: false,
dataDownload: true,
adapter: Report::TicketReopened,
},
{
name: 'movedin',
display: 'Moved in',
selected: false,
dataDownload: true,
adapter: Report::TicketMoved,
params: { type: 'in' },
},
{
name: 'movedout',
display: 'Moved out',
selected: false,
dataDownload: true,
adapter: Report::TicketMoved,
params: { type: 'out' },
},
{
name: 'sla_in',
display: 'SLA in',
selected: false,
dataDownload: true,
},
{
name: 'sla_out',
display: 'SLA out',
selected: false,
dataDownload: true,
},
#{
# name: 'sla_in',
# display: 'SLA in',
# selected: false,
# dataDownload: true,
#},
#{
# name: 'sla_out',
# display: 'SLA out',
# selected: false,
# dataDownload: true,
#},
]
config[:metric][:count][:backend] = backend
@ -79,24 +89,80 @@ class Report
display: 'Phone (in)',
selected: true,
dataDownload: true,
adapter: Report::TicketGenericTime,
params: {
field: 'created_at',
selector: {
'create_article_type_id' => {
'operator' => 'is',
'value' => Ticket::Article::Type.lookup(name: 'phone').id,
},
'create_article_sender_id' => {
'operator' => 'is',
'value' => Ticket::Article::Sender.lookup(name: 'Customer').id,
},
},
},
},
{
name: 'phone_out',
display: 'Phone (out)',
selected: true,
dataDownload: true,
adapter: Report::TicketGenericTime,
params: {
field: 'created_at',
selector: {
'create_article_type_id' => {
'operator' => 'is',
'value' => Ticket::Article::Type.lookup(name: 'phone').id,
},
'create_article_sender_id' => {
'operator' => 'is',
'value' => Ticket::Article::Sender.lookup(name: 'Agent').id,
},
}
},
},
{
name: 'email_in',
display: 'Email (in)',
selected: true,
dataDownload: true,
adapter: Report::TicketGenericTime,
params: {
field: 'created_at',
selector: {
'create_article_type_id' => {
'operator' => 'is',
'value' => Ticket::Article::Type.lookup(name: 'email').id,
},
'create_article_sender_id' => {
'operator' => 'is',
'value' => Ticket::Article::Sender.lookup(name: 'Customer').id,
},
},
},
},
{
name: 'email_out',
display: 'Email (out)',
selected: true,
dataDownload: true,
adapter: Report::TicketGenericTime,
params: {
field: 'created_at',
selector: {
'create_article_type_id' => {
'operator' => 'is',
'value' => Ticket::Article::Type.lookup(name: 'email').id,
},
'create_article_sender_id' => {
'operator' => 'is',
'value' => Ticket::Article::Sender.lookup(name: 'Agent').id,
},
},
},
},
{
name: 'web_in',
@ -218,19 +284,19 @@ class Report
{
name: 'sla_out_1',
display: 'SLA (out) - <1h',
selected: true,
selected: false,
dataDownload: true,
},
{
name: 'sla_out_2',
display: 'SLA (out) - <2h',
selected: true,
selected: false,
dataDownload: true,
},
{
name: 'sla_out_4',
display: 'SLA (out) - <4h',
selected: true,
selected: false,
dataDownload: true,
},
{
@ -248,19 +314,19 @@ class Report
{
name: 'sla_in_2',
display: 'SLA (in) - <2h',
selected: true,
selected: false,
dataDownload: true,
},
{
name: 'sla_in_4',
display: 'SLA (in) - <4h',
selected: true,
selected: false,
dataDownload: true,
},
{
name: 'sla_in_8',
display: 'SLA (in) - <8h',
selected: true,
selected: false,
dataDownload: true,
},
]

389
lib/report/base.rb Normal file
View file

@ -0,0 +1,389 @@
class Report::Base
# :object
# :type created|updated
# :attribute
# :value_from
# :value_to
# :start
# :end
# :selector
def self.history_count(params)
history_object = History::Object.lookup( name: params[:object] )
query, bind_params, tables = Ticket.selector2sql(params[:selector])
count = 0
ticket_ids = []
# created
if params[:type] == 'created'
history_type = History::Type.lookup( name: 'created' )
return History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
.where(
'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
)
.where(query, *bind_params).joins(tables).count
end
# updated
if params[:type] == 'updated'
history_type = History::Type.lookup( name: 'updated' )
history_attribute = History::Attribute.lookup( name: params[:attribute] )
if !history_attribute || !history_type
count = 0
else
if params[:id_not_from] && params[:id_to]
return History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
.where(query, *bind_params).joins(tables)
.where(
'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 (?)',
params[:start],
params[:end],
history_object.id,
history_type.id,
history_attribute.id,
params[:id_not_from],
params[:id_to],
).count
elsif params[:id_from] && params[:id_not_to]
return History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
.where(query, *bind_params).joins(tables)
.where(
'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 (?)',
params[:start],
params[:end],
history_object.id,
history_type.id,
history_attribute.id,
params[:id_from],
params[:id_not_to],
).count
elsif params[:value_from] && params[:value_not_to]
return History.joins('INNER JOIN tickets ON tickets.id = histories.o_id')
.where(query, *bind_params).joins(tables)
.where(
'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 (?)',
params[:start],
params[:end],
history_object.id,
history_type.id,
history_attribute.id,
params[:value_from],
params[:value_not_to],
).count
elsif params[:value_to]
return History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
.where(query, *bind_params).joins(tables)
.where(
'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 (?)',
params[:start],
params[:end],
history_object.id,
history_type.id,
history_attribute.id,
params[:value_to],
).count
elsif params[:id_to]
return History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
.where(query, *bind_params).joins(tables)
.where(
'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 (?)',
params[:start],
params[:end],
history_object.id,
history_type.id,
history_attribute.id,
params[:id_to],
).count
else
fail "UNKOWN params (#{params.inspect})!"
end
end
end
fail "UNKOWN :type (#{params[:type]})!"
end
# :object
# :type created|updated
# :attribute
# :value_from
# :value_to
# :start
# :end
# :condition
def self.history(data)
history_object = History::Object.lookup( name: data[:object] )
query, bind_params, tables = Ticket.selector2sql(data[:selector])
count = 0
ticket_ids = []
# created
if data[:type] == 'created'
history_type = History::Type.lookup( name: 'created' )
histories = History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
.where(
'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
)
.where(query, *bind_params).joins(tables)
histories.each {|history|
count += 1
ticket_ids.push history.o_id
}
return {
count: count,
ticket_ids: ticket_ids,
}
end
# updated
if data[:type] == 'updated'
history_type = History::Type.lookup( name: 'updated' )
history_attribute = History::Attribute.lookup( name: data[:attribute] )
if !history_attribute || !history_type
count = 0
else
if data[:id_not_from] && data[:id_to]
histories = History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
.where(query, *bind_params).joins(tables)
.where(
'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 (?)',
data[:start],
data[:end],
history_object.id,
history_type.id,
history_attribute.id,
data[:id_not_from],
data[:id_to],
)
elsif data[:id_from] && data[:id_not_to]
histories = History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
.where(query, *bind_params).joins(tables)
.where(
'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 (?)',
data[:start],
data[:end],
history_object.id,
history_type.id,
history_attribute.id,
data[:id_from],
data[:id_not_to],
)
elsif data[:value_from] && data[:value_not_to]
histories = History.joins('INNER JOIN tickets ON tickets.id = histories.o_id')
.where(query, *bind_params).joins(tables)
.where(
'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 (?)',
data[:start],
data[:end],
history_object.id,
history_type.id,
history_attribute.id,
data[:value_from],
data[:value_not_to],
)
elsif data[:value_to]
histories = History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
.where(query, *bind_params).joins(tables)
.where(
'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 (?)',
data[:start],
data[:end],
history_object.id,
history_type.id,
history_attribute.id,
data[:value_to],
)
elsif data[:id_to]
histories = History.select('histories.o_id').joins('INNER JOIN tickets ON tickets.id = histories.o_id')
.where(query, *bind_params).joins(tables)
.where(
'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 (?)',
data[:start],
data[:end],
history_object.id,
history_type.id,
history_attribute.id,
data[:id_to],
)
end
histories.each {|history|
count += 1
ticket_ids.push history.o_id
}
end
return {
count: count,
ticket_ids: ticket_ids,
}
end
fail "UNKOWN :type (#{data[:type]})!"
end
# :sender
# :type
# :start
# :end
# :condition
def self.article_type_and_sender(data)
query, bind_params, tables = Ticket.selector2sql(data[:condition])
sender = Ticket::Article::Sender.lookup( name: data[:sender] )
type = Ticket::Article::Type.lookup( name: data[:type] )
articles = Ticket::Article.joins('INNER JOIN tickets ON tickets.id = ticket_articles.ticket_id')
.where(query, *bind_params).joins(tables)
.where(
'ticket_articles.created_at >= ? AND ticket_articles.created_at <= ? AND ticket_articles.type_id = ? AND ticket_articles.sender_id = ?',
data[:start],
data[:end],
type.id,
sender.id,
).count
{
count: articles,
}
end
# :type
# :start
# :end
# :condition
def self.create_channel(data)
query, bind_params, tables = Ticket.selector2sql(data[:condition])
article_type = Ticket::Article::Type.lookup( name: data[:type] )
tickets = Ticket.select('tickets.id')
.where( 'tickets.created_at >= ? AND tickets.created_at <= ? AND tickets.create_article_type_id = ?', data[:start], data[:end], article_type.id )
.where(query, *bind_params).joins(tables)
count = 0
ticket_ids = []
tickets.each {|ticket|
count += 1
ticket_ids.push ticket.id
}
{
count: count,
ticket_ids: ticket_ids,
}
end
# :type
# :start
# :end
# :condition
def self.time_average(data)
query, bind_params, tables = Ticket.selector2sql(data[:condition])
ticket_list = Ticket.where( 'tickets.created_at >= ? AND tickets.created_at <= ?', data[:start], data[:end] )
.where(query, *bind_params).joins(tables)
tickets = 0
time_total = 0
ticket_list.each {|ticket|
timestamp = ticket[ data[:type].to_sym ]
next if !timestamp
# puts 'FR:' + first_response.to_s
# puts 'CT:' + ticket.created_at.to_s
diff = timestamp - ticket.created_at
#puts 'DIFF:' + diff.to_s
time_total = time_total + diff
tickets += 1
}
if time_total == 0 || tickets == 0
tickets = -0.001
else
tickets = time_total / tickets / 60
tickets = tickets.to_i
end
{
count: tickets,
}
end
# :type
# :start
# :end
# :condition
def self.time_min(data)
query, bind_params, tables = Ticket.selector2sql(data[:condition])
ticket_list = Ticket.where( 'tickets.created_at >= ? AND tickets.created_at <= ?', data[:start], data[:end] )
.where(query, *bind_params).joins(tables)
tickets = 0
time_min = 0
ticket_ids = []
ticket_list.each {|ticket|
timestamp = ticket[ data[:type].to_sym ]
next if !timestamp
ticket_ids.push ticket.id
# puts 'FR:' + first_response.to_s
# puts 'CT:' + ticket.created_at.to_s
diff = timestamp - ticket.created_at
#puts 'DIFF:' + diff.to_s
if !time_min
time_min = diff
end
if diff < time_min
time_min = diff
end
}
if time_min == 0
tickets = -0.001
else
tickets = (time_min / 60).to_i
end
{
count: tickets,
ticket_ids: ticket_ids,
}
end
# :type
# :start
# :end
# :condition
def self.time_max(data)
query, bind_params, tables = Ticket.selector2sql(data[:condition])
ticket_list = Ticket.where( 'tickets.created_at >= ? AND tickets.created_at <= ?', data[:start], data[:end] )
.where(query, *bind_params).joins(tables)
tickets = 0
time_max = 0
ticket_ids = []
ticket_list.each {|ticket|
timestamp = ticket[ data[:type].to_sym ]
next if !timestamp
ticket_ids.push ticket.id
# puts "#{data[:type].to_s} - #{timestamp} - #{ticket.inspect}"
# puts 'FR:' + ticket.first_response.to_s
# puts 'CT:' + ticket.created_at.to_s
diff = timestamp - ticket.created_at
#puts 'DIFF:' + diff.to_s
if !time_max
time_max = diff
end
if diff > time_max
time_max = diff
end
}
if time_max == 0
tickets = -0.001
else
tickets = (time_max / 60).to_i
end
{
count: tickets,
ticket_ids: ticket_ids,
}
end
def self.ticket_condition(ticket_id, condition)
ticket = Ticket.lookup( id: ticket_id )
match = true
condition.each {|key, value|
if ticket[key.to_sym] != value
return false
end
}
true
end
end

View file

@ -0,0 +1,118 @@
class Report::TicketFirstSolution
=begin
result = Report::TicketFirstSolution.aggs(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
interval: 'month', # quarter, month, week, day, hour, minute, second
selector: selector, # ticket selector to get only a collection of tickets
)
returns
[4,5,1,5,0,51,5,56,7,4]
=end
def self.aggs(params)
interval = params[:interval]
if params[:interval] == 'week'
interval = 'day'
end
result = []
if params[:interval] == 'month'
start = Date.parse(params[:range_start])
stop_interval = 12
elsif params[:interval] == 'week'
start = Date.parse(params[:range_start])
stop_interval = 7
elsif params[:interval] == 'day'
start = Date.parse(params[:range_start])
stop_interval = 31
elsif params[:interval] == 'hour'
start = Time.zone.parse(params[:range_start])
stop_interval = 24
elsif params[:interval] == 'minute'
start = Time.zone.parse(params[:range_start])
stop_interval = 60
end
(1..stop_interval).each {|_counter|
if params[:interval] == 'month'
stop = start.next_month
elsif params[:interval] == 'week'
stop = start.next_day
elsif params[:interval] == 'day'
stop = start.next_day
elsif params[:interval] == 'hour'
stop = start + 1.hour
elsif params[:interval] == 'minute'
stop = start + 1.minute
end
query, bind_params, tables = Ticket.selector2sql(params[:selector])
ticket_list = Ticket.select('tickets.id, tickets.close_time, tickets.created_at').where(
'tickets.close_time IS NOT NULL AND tickets.close_time >= ? AND tickets.close_time < ?',
start,
stop,
).where(query, *bind_params).joins(tables)
count = 0
ticket_list.each {|ticket|
closed_at = ticket.close_time
created_at = ticket.created_at
if (closed_at - (60 * 15) ) < created_at
count += 1
end
}
result.push count
start = stop
}
result
end
=begin
result = Report::TicketFirstSolution.items(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
selector: selector, # ticket selector to get only a collection of tickets
)
returns
{
count: 123,
ticket_ids: [4,5,1,5,0,51,5,56,7,4],
assets: assets,
}
=end
def self.items(params)
query, bind_params, tables = Ticket.selector2sql(params[:selector])
ticket_list = Ticket.select('tickets.id, tickets.close_time, tickets.created_at').where(
'tickets.close_time IS NOT NULL AND tickets.close_time >= ? AND tickets.close_time < ?',
params[:range_start],
params[:range_end],
).where(query, *bind_params).joins(tables)
count = 0
assets = {}
ticket_ids = []
ticket_list.each {|ticket|
closed_at = ticket.close_time
created_at = ticket.created_at
if (closed_at - (60 * 15) ) < created_at
count += 1
ticket_ids.push ticket.id
end
ticket_full = Ticket.find(ticket.id)
assets = ticket_full.assets(assets)
}
{
count: count,
ticket_ids: ticket_ids,
assets: assets,
}
end
end

View file

@ -0,0 +1,131 @@
class Report::TicketGenericTime
=begin
selector = {}
result = Report::TicketGenericTime.aggs(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
interval: 'month', # year, quarter, month, week, day, hour, minute, second
selector: selector, # ticket selector to get only a collection of tickets
params: { field: 'created_at' },
)
returns
[4,5,1,5,0,51,5,56,7,4]
=end
def self.aggs(params)
interval_es = params[:interval]
if params[:interval] == 'week'
interval_es = 'day'
end
aggs_interval = {
from: params[:range_start],
to: params[:range_end],
interval: interval_es, # year, quarter, month, week, day, hour, minute, second
field: params[:params][:field],
}
result_es = SearchIndexBackend.selectors(['Ticket'], params[:selector], nil, nil, aggs_interval)
if params[:interval] == 'month'
start = Date.parse(params[:range_start])
stop_interval = 12
elsif params[:interval] == 'week'
start = Date.parse(params[:range_start])
stop_interval = 7
elsif params[:interval] == 'day'
start = Date.parse(params[:range_start])
stop_interval = 31
elsif params[:interval] == 'hour'
start = Time.zone.parse(params[:range_start])
stop_interval = 24
elsif params[:interval] == 'minute'
start = Time.zone.parse(params[:range_start])
stop_interval = 60
end
result = []
(1..stop_interval).each {|_counter|
match = false
result_es['aggregations']['time_buckets']['buckets'].each {|item|
if params[:interval] == 'minute'
item['key_as_string'] = item['key_as_string'].sub(/:\d\d.\d\d\dZ$/, '')
start_string = start.iso8601.sub(/:\d\dZ$/, '')
else
start_string = start.iso8601.sub(/:\d\d:\d\d.+?$/, '')
end
next if !item['doc_count']
next if item['key_as_string'] !~ /#{start_string}/
next if match
match = true
result.push item['doc_count']
if params[:interval] == 'month'
start = start.next_month
elsif params[:interval] == 'week'
start = start.next_day
elsif params[:interval] == 'day'
start = start.next_day
elsif params[:interval] == 'hour'
start = start + 1.hour
elsif params[:interval] == 'minute'
start = start + 1.minute
end
}
next if match
result.push 0
if params[:interval] == 'month'
start = start.next_month
elsif params[:interval] == 'week'
start = start.next_day
elsif params[:interval] == 'day'
start = start + 1.day
elsif params[:interval] == 'hour'
start = start + 1.hour
elsif params[:interval] == 'minute'
start = start + 1.minute
end
}
result
end
=begin
result = Report::TicketGenericTime.items(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
selector: selector, # ticket selector to get only a collection of tickets
params: { field: 'created_at' },
)
returns
{
count: 123,
ticket_ids: [4,5,1,5,0,51,5,56,7,4],
assets: assets,
}
=end
def self.items(params)
aggs_interval = {
from: params[:range_start],
to: params[:range_end],
field: params[:params][:field],
}
result = SearchIndexBackend.selectors(['Ticket'], params[:selector], nil, nil, aggs_interval)
assets = {}
result[:ticket_ids].each {|ticket_id|
ticket_full = Ticket.find(ticket_id)
assets = ticket_full.assets(assets)
}
result[:assets] = assets
result
end
end

165
lib/report/ticket_moved.rb Normal file
View file

@ -0,0 +1,165 @@
class Report::TicketMoved < Report::Base
=begin
result = Report::TicketMoved.aggs(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
interval: 'month', # quarter, month, week, day, hour, minute, second
selector: selector, # ticket selector to get only a collection of tickets
params: { type: 'in' }, # in|out
)
returns
[4,5,1,5,0,51,5,56,7,4]
=end
def self.aggs(params)
selector = params[:selector]['ticket.group_id']
if !selector
return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
end
interval = params[:interval]
if params[:interval] == 'week'
interval = 'day'
end
result = []
if params[:interval] == 'month'
start = Date.parse(params[:range_start])
stop_interval = 12
elsif params[:interval] == 'week'
start = Date.parse(params[:range_start])
stop_interval = 7
elsif params[:interval] == 'day'
start = Date.parse(params[:range_start])
stop_interval = 31
elsif params[:interval] == 'hour'
start = Time.zone.parse(params[:range_start])
stop_interval = 24
elsif params[:interval] == 'minute'
start = Time.zone.parse(params[:range_start])
stop_interval = 60
end
(1..stop_interval).each {|_counter|
if params[:interval] == 'month'
stop = start.next_month
elsif params[:interval] == 'week'
stop = start.next_day
elsif params[:interval] == 'day'
stop = start.next_day
elsif params[:interval] == 'hour'
stop = start + 1.hour
elsif params[:interval] == 'minute'
stop = start + 1.minute
end
local_params = group_attributes(selector, params)
local_selector = params[:selector].clone
if params[:params][:type] == 'out'
local_selector.delete('ticket.group_id')
end
defaults = {
object: 'Ticket',
type: 'updated',
attribute: 'group',
start: start,
end: stop,
selector: local_selector
}
local_params = defaults.merge(local_params)
count = history_count(local_params)
result.push count
start = stop
}
result
end
=begin
result = Report::TicketMoved.items(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
selector: selector, # ticket selector to get only a collection of tickets
params: { type: 'in' }, # in|out
)
returns
{
count: 123,
ticket_ids: [4,5,1,5,0,51,5,56,7,4],
assets: assets,
}
=end
def self.items(params)
selector = params[:selector]['ticket.group_id']
if !selector
return {
count: 0,
ticket_ids: [],
}
end
local_params = group_attributes(selector, params)
local_selector = params[:selector].clone
if params[:params][:type] == 'out'
local_selector.delete('ticket.group_id')
end
defaults = {
object: 'Ticket',
type: 'updated',
attribute: 'group',
start: params[:range_start],
end: params[:range_end],
selector: local_selector
}
local_params = defaults.merge(local_params)
result = history(local_params)
assets = {}
result[:ticket_ids].each {|ticket_id|
ticket_full = Ticket.find(ticket_id)
assets = ticket_full.assets(assets)
}
result[:assets] = assets
result
end
def self.group_attributes(selector, params)
if selector['operator'] == 'is'
group_id = selector['value']
if params[:params][:type] == 'in'
return {
id_not_from: group_id,
id_to: group_id,
}
else
return {
id_from: group_id,
id_not_to: group_id,
}
end
else
group_id = selector['value']
if params[:params][:type] == 'in'
return {
id_from: group_id,
id_not_to: group_id,
}
else
return {
id_not_from: group_id,
id_to: group_id,
}
end
end
fail "Unknown selector params '#{selector.inspect}'"
end
end

View file

@ -0,0 +1,124 @@
class Report::TicketReopened < Report::Base
=begin
result = Report::TicketReopened.aggs(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
interval: 'month', # quarter, month, week, day, hour, minute, second
selector: selector, # ticket selector to get only a collection of tickets
)
returns
[4,5,1,5,0,51,5,56,7,4]
=end
def self.aggs(params)
ticket_state_ids = ticket_ids
interval = params[:interval]
if params[:interval] == 'week'
interval = 'day'
end
result = []
if params[:interval] == 'month'
start = Date.parse(params[:range_start])
stop_interval = 12
elsif params[:interval] == 'week'
start = Date.parse(params[:range_start])
stop_interval = 7
elsif params[:interval] == 'day'
start = Date.parse(params[:range_start])
stop_interval = 31
elsif params[:interval] == 'hour'
start = Time.zone.parse(params[:range_start])
stop_interval = 24
elsif params[:interval] == 'minute'
start = Time.zone.parse(params[:range_start])
stop_interval = 60
end
(1..stop_interval).each {|_counter|
if params[:interval] == 'month'
stop = start.next_month
elsif params[:interval] == 'week'
stop = start.next_day
elsif params[:interval] == 'day'
stop = start.next_day
elsif params[:interval] == 'hour'
stop = start + 1.hour
elsif params[:interval] == 'minute'
stop = start + 1.minute
end
count = history_count(
object: 'Ticket',
type: 'updated',
attribute: 'state',
id_from: ticket_state_ids,
id_not_to: ticket_state_ids,
start: start,
end: stop,
selector: params[:selector]
)
result.push count
start = stop
}
result
end
=begin
result = Report::TicketReopened.items(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
selector: selector, # ticket selector to get only a collection of tickets
)
returns
{
count: 123,
ticket_ids: [4,5,1,5,0,51,5,56,7,4],
assets: assets,
}
=end
def self.items(params)
ticket_state_ids = ticket_ids
result = history(
object: 'Ticket',
type: 'updated',
attribute: 'state',
id_from: ticket_state_ids,
id_not_to: ticket_state_ids,
start: params[:range_start],
end: params[:range_end],
selector: params[:selector]
)
assets = {}
result[:ticket_ids].each {|ticket_id|
ticket_full = Ticket.find(ticket_id)
assets = ticket_full.assets(assets)
}
result[:assets] = assets
result
end
def self.ticket_ids
key = 'Report::TicketReopened::StateList'
ticket_state_ids = Cache.get( key )
return ticket_state_ids if ticket_state_ids
ticket_state_types = Ticket::StateType.where( name: %w(closed merged removed) )
ticket_state_ids = []
ticket_state_types.each {|ticket_state_type|
ticket_state_type.states.each {|ticket_state|
ticket_state_ids.push ticket_state.id
}
}
Cache.write( key, ticket_state_ids, { expires_in: 2.days } )
ticket_state_ids
end
end

View file

@ -232,19 +232,18 @@ return search result
=begin
return aggregation result
get count of tickets and tickets which match on selector
result = SearchIndexBackend.aggs(
{
title: 'test',
state_id: 4,
},
['2014-10-19', '2015-10-19', 'created_at', 'month'],
['Ticket'],
)
aggs_interval = {
from: '2015-01-01',
to: '2015-12-31',
interval: 'month', # year, quarter, month, week, day, hour, minute, second
field: 'created_at',
}
# year, quarter, month, week, day, hour, minute, second
result = SearchIndexBackend.selectors(index, params[:condition], limit, current_user, aggs_interval)
# for aggregations
result = {
hits:{
total:4819,
@ -270,7 +269,8 @@ return aggregation result
=end
def self.aggs(query, range, index = nil)
def self.selectors(index = nil, selectors = nil, _limit = 10, current_user = nil, aggs_interval = nil)
fail 'no selectors given' if !selectors
url = build_url()
return if !url
@ -284,45 +284,7 @@ return aggregation result
url += '/_search'
end
and_data = []
if query && !query.empty?
bool = {
bool: {
must: {
term: query,
},
},
}
and_data.push bool
end
range_data = {}
range_data[range[2]] = {
from: range[0],
to: range[1],
}
range_data_and = {
range: range_data,
}
and_data.push range_data_and
data = {
query: {
filtered: {
filter: {
and: and_data,
}
}
},
size: 0,
aggs: {
time_buckets: {
date_histogram: {
field: range[2],
interval: range[3],
}
}
}
}
data = selector2query(selectors, current_user, aggs_interval)
Rails.logger.info "# curl -X POST \"#{url}\" \\"
Rails.logger.debug " -d'#{data.to_json}'"
@ -345,9 +307,114 @@ return aggregation result
return []
end
Rails.logger.debug response.data.to_json
if !aggs_interval || !aggs_interval[:interval]
ticket_ids = []
response.data['hits']['hits'].each {|item|
ticket_ids.push item['_id']
}
return {
count: response.data['hits']['total'],
ticket_ids: ticket_ids,
}
end
response.data
end
def self.selector2query(selector, _current_user, aggs_interval)
filter_must = []
filter_must_not = []
query_must = []
query_must_not = []
if selector && !selector.empty?
selector.each {|key, data|
key_tmp = key.sub(/^.+?\./, '')
t = {}
if data['value'].class == Array
t[:terms] = {}
t[:terms][key_tmp] = data['value']
else
t[:term] = {}
t[:term][key_tmp] = data['value']
end
if data['operator'] == 'is'
filter_must.push t
elsif data['operator'] == 'is not'
filter_must_not.push t
elsif data['operator'] == 'contains'
query_must.push t
elsif data['operator'] == 'contains not'
query_must_not.push t
else
fail "unknown operator '#{data['operator']}'"
end
}
end
data = {
query: {}
}
# add aggs to filter
if aggs_interval
if aggs_interval[:interval]
data[:size] = 0
data[:aggs] = {
time_buckets: {
date_histogram: {
field: aggs_interval[:field],
interval: aggs_interval[:interval],
}
}
}
end
r = {}
r[:range] = {}
r[:range][aggs_interval[:field]] = {
from: aggs_interval[:from],
to: aggs_interval[:to],
}
filter_must.push r
end
if !query_must.empty? || !query_must_not.empty?
if !data[:query][:filtered]
data[:query][:filtered] = {}
end
if !data[:query][:filtered][:query]
data[:query][:filtered][:query] = {}
end
if !data[:query][:filtered][:query][:bool]
data[:query][:filtered][:query][:bool] = {}
end
end
if !query_must.empty?
data[:query][:filtered][:query][:bool][:must] = query_must
end
if !query_must_not.empty?
data[:query][:filtered][:query][:bool][:must_not] = query_must_not
end
if !filter_must.empty? || !filter_must.empty?
if !data[:query][:filtered]
data[:query][:filtered] = {}
end
if !data[:query][:filtered][:filter]
data[:query][:filtered][:filter] = {}
end
if !data[:query][:filtered][:filter][:bool]
data[:query][:filtered][:filter][:bool] = {}
end
end
if !filter_must.empty?
data[:query][:filtered][:filter][:bool][:must] = filter_must
end
if !filter_must_not.empty?
data[:query][:filtered][:filter][:bool][:must_not] = filter_must_not
end
data
end
=begin
return true if backend is configured

View file

@ -0,0 +1,747 @@
# encoding: utf-8
require 'integration_test_helper'
class ReportTest < ActiveSupport::TestCase
# set config
if !ENV['ES_URL']
fail "ERROR: Need ES_URL - hint ES_URL='http://172.0.0.1:9200'"
end
Setting.set('es_url', ENV['ES_URL'])
if !ENV['ES_INDEX']
fail "ERROR: Need ES_INDEX - hint ES_INDEX='estest.local_zammad'"
end
Setting.set('es_index', ENV['ES_INDEX'])
# Setting.set('es_url', 'http://172.0.0.1:9200')
# Setting.set('es_index', 'estest.local_zammad')
# Setting.set('es_user', 'elasticsearch')
# Setting.set('es_password', 'zammad')
# Setting.set('es_attachment_max_size_in_mb', 1 )
Ticket.destroy_all
# drop/create indexes
#Rake::Task["searchindex:drop"].execute
#Rake::Task["searchindex:create"].execute
#system('rake searchindex:rebuild')
Group.create_if_not_exists(
name: 'Report Test',
updated_by_id: 1,
created_by_id: 1
)
ticket1 = Ticket.create(
title: 'test 1',
group: Group.lookup(name: 'Report Test'),
customer_id: 2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
created_at: '2015-10-28 09:30:00 UTC',
updated_at: '2015-10-28 09:30:00 UTC',
updated_by_id: 1,
created_by_id: 1,
)
article1 = Ticket::Article.create(
ticket_id: ticket1.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject',
message_id: 'some@id',
body: 'some message article_inbound',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Customer').first,
type: Ticket::Article::Type.where(name: 'email').first,
created_at: '2015-10-28 09:30:00 UTC',
updated_at: '2015-10-28 09:30:00 UTC',
updated_by_id: 1,
created_by_id: 1,
)
ticket1.update_attributes(
group: Group.lookup(name: 'Users'),
updated_at: '2015-10-28 14:30:00 UTC',
)
ticket2 = Ticket.create(
title: 'test 2',
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
created_at: '2015-10-28 09:30:00 UTC',
updated_at: '2015-10-28 09:30:00 UTC',
updated_by_id: 1,
created_by_id: 1,
)
article2 = Ticket::Article.create(
ticket_id: ticket2.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject',
message_id: 'some@id',
body: 'some message article_inbound',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Customer').first,
type: Ticket::Article::Type.where(name: 'email').first,
created_at: '2015-10-28 09:30:00 UTC',
updated_at: '2015-10-28 09:30:00 UTC',
updated_by_id: 1,
created_by_id: 1,
)
ticket2.update_attributes(
group_id: Group.lookup(name: 'Report Test').id,
updated_at: '2015-10-28 14:30:00 UTC',
)
ticket3 = Ticket.create(
title: 'test 3',
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'open'),
priority: Ticket::Priority.lookup(name: '3 high'),
created_at: '2015-10-28 10:30:00 UTC',
updated_at: '2015-10-28 10:30:00 UTC',
updated_by_id: 1,
created_by_id: 1,
)
article3 = Ticket::Article.create(
ticket_id: ticket3.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject',
message_id: 'some@id',
body: 'some message article_inbound',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Customer').first,
type: Ticket::Article::Type.where(name: 'email').first,
created_at: '2015-10-28 10:30:00 UTC',
updated_at: '2015-10-28 10:30:00 UTC',
updated_by_id: 1,
created_by_id: 1,
)
ticket4 = Ticket.create(
title: 'test 4',
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'closed'),
priority: Ticket::Priority.lookup(name: '2 normal'),
close_time: '2015-10-28 11:30:00 UTC',
created_at: '2015-10-28 10:30:00 UTC',
updated_at: '2015-10-28 10:30:00 UTC',
updated_by_id: 1,
created_by_id: 1,
)
article4 = Ticket::Article.create(
ticket_id: ticket4.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject',
message_id: 'some@id',
body: 'some message article_inbound',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Customer').first,
type: Ticket::Article::Type.where(name: 'email').first,
created_at: '2015-10-28 10:30:00 UTC',
updated_at: '2015-10-28 10:30:00 UTC',
updated_by_id: 1,
created_by_id: 1,
)
ticket5 = Ticket.create(
title: 'test 5',
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'closed'),
priority: Ticket::Priority.lookup(name: '3 high'),
close_time: '2015-10-28 11:40:00 UTC',
created_at: '2015-10-28 11:30:00 UTC',
updated_at: '2015-10-28 11:30:00 UTC',
updated_by_id: 1,
created_by_id: 1,
)
article5 = Ticket::Article.create(
ticket_id: ticket5.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject',
message_id: 'some@id',
body: 'some message article_outbound',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
created_at: '2015-10-28 11:30:00 UTC',
updated_at: '2015-10-28 11:30:00 UTC',
updated_by_id: 1,
created_by_id: 1,
)
ticket5.update_attributes(
state: Ticket::State.lookup(name: 'open'),
updated_at: '2015-10-28 14:30:00 UTC',
)
ticket6 = Ticket.create(
title: 'test 6',
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'closed'),
priority: Ticket::Priority.lookup(name: '2 normal'),
close_time: '2015-10-31 12:35:00 UTC',
created_at: '2015-10-31 12:30:00 UTC',
updated_at: '2015-10-31 12:30:00 UTC',
updated_by_id: 1,
created_by_id: 1,
)
article6 = Ticket::Article.create(
ticket_id: ticket6.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject',
message_id: 'some@id',
body: 'some message article_outbound',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
created_at: '2015-10-31 12:30:00 UTC',
updated_at: '2015-10-31 12:30:00 UTC',
updated_by_id: 1,
created_by_id: 1,
)
ticket7 = Ticket.create(
title: 'test 7',
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'closed'),
priority: Ticket::Priority.lookup(name: '2 normal'),
close_time: '2015-11-01 12:30:00 UTC',
created_at: '2015-11-01 12:30:00 UTC',
updated_at: '2015-11-01 12:30:00 UTC',
updated_by_id: 1,
created_by_id: 1,
)
article7 = Ticket::Article.create(
ticket_id: ticket7.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'some subject',
message_id: 'some@id',
body: 'some message article_outbound',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
created_at: '2015-11-01 12:30:00 UTC',
updated_at: '2015-11-01 12:30:00 UTC',
updated_by_id: 1,
created_by_id: 1,
)
# execute background jobs
#puts Delayed::Job.all.inspect
Delayed::Worker.new.work_off
sleep 6
test 'first solution' do
# month
result = Report::TicketFirstSolution.aggs(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
interval: 'month', # year, quarter, month, week, day, hour, minute, second
selector: {}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(0, result[0])
assert_equal(0, result[1])
assert_equal(0, result[2])
assert_equal(0, result[3])
assert_equal(0, result[4])
assert_equal(0, result[5])
assert_equal(0, result[6])
assert_equal(0, result[7])
assert_equal(0, result[8])
assert_equal(2, result[9])
assert_equal(1, result[10])
assert_equal(0, result[11])
assert_equal(nil, result[12])
result = Report::TicketFirstSolution.items(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
selector: {}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(ticket5.id, result[:ticket_ids][0])
assert_equal(ticket6.id, result[:ticket_ids][1])
assert_equal(ticket7.id, result[:ticket_ids][2])
assert_equal(nil, result[:ticket_ids][3])
# month - with selector #1
result = Report::TicketFirstSolution.aggs(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
interval: 'month', # year, quarter, month, week, day, hour, minute, second
selector: {
'ticket.priority_id' => {
'operator' => 'is',
'value' => [Ticket::Priority.lookup(name: '3 high').id],
}
}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(0, result[0])
assert_equal(0, result[1])
assert_equal(0, result[2])
assert_equal(0, result[3])
assert_equal(0, result[4])
assert_equal(0, result[5])
assert_equal(0, result[6])
assert_equal(0, result[7])
assert_equal(0, result[8])
assert_equal(1, result[9])
assert_equal(0, result[10])
assert_equal(0, result[11])
assert_equal(nil, result[12])
result = Report::TicketFirstSolution.items(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
selector: {
'ticket.priority_id' => {
'operator' => 'is',
'value' => [Ticket::Priority.lookup(name: '3 high').id],
}
}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(ticket5.id, result[:ticket_ids][0])
assert_equal(nil, result[:ticket_ids][1])
# month - with selector #2
result = Report::TicketFirstSolution.aggs(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
interval: 'month', # year, quarter, month, week, day, hour, minute, second
selector: {
'ticket.priority_id' => {
'operator' => 'is not',
'value' => [Ticket::Priority.lookup(name: '3 high').id],
}
}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(0, result[0])
assert_equal(0, result[1])
assert_equal(0, result[2])
assert_equal(0, result[3])
assert_equal(0, result[4])
assert_equal(0, result[5])
assert_equal(0, result[6])
assert_equal(0, result[7])
assert_equal(0, result[8])
assert_equal(1, result[9])
assert_equal(1, result[10])
assert_equal(0, result[11])
assert_equal(nil, result[12])
result = Report::TicketFirstSolution.items(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
selector: {
'ticket.priority_id' => {
'operator' => 'is not',
'value' => [Ticket::Priority.lookup(name: '3 high').id],
}
}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(ticket6.id, result[:ticket_ids][0])
assert_equal(ticket7.id, result[:ticket_ids][1])
assert_equal(nil, result[:ticket_ids][2])
# week
result = Report::TicketFirstSolution.aggs(
range_start: '2015-10-26T00:00:00Z',
range_end: '2015-10-31T23:59:59Z',
interval: 'week', # year, quarter, month, week, day, hour, minute, second
selector: {}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(0, result[0])
assert_equal(0, result[1])
assert_equal(1, result[2])
assert_equal(0, result[3])
assert_equal(0, result[4])
assert_equal(1, result[5])
assert_equal(1, result[6])
assert_equal(nil, result[7])
result = Report::TicketFirstSolution.items(
range_start: '2015-10-26T00:00:00Z',
range_end: '2015-11-01T23:59:59Z',
interval: 'week', # year, quarter, month, week, day, hour, minute, second
selector: {}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(ticket5.id, result[:ticket_ids][0])
assert_equal(ticket6.id, result[:ticket_ids][1])
assert_equal(ticket7.id, result[:ticket_ids][2])
assert_equal(nil, result[:ticket_ids][3])
# day
result = Report::TicketFirstSolution.aggs(
range_start: '2015-10-01T00:00:00Z',
range_end: '2015-11-01T23:59:59Z',
interval: 'day', # year, quarter, month, week, day, hour, minute, second
selector: {}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(0, result[0])
assert_equal(0, result[1])
assert_equal(0, result[2])
assert_equal(0, result[3])
assert_equal(0, result[4])
assert_equal(0, result[5])
assert_equal(0, result[6])
assert_equal(0, result[7])
assert_equal(0, result[8])
assert_equal(0, result[9])
assert_equal(0, result[10])
assert_equal(0, result[11])
assert_equal(0, result[12])
assert_equal(0, result[13])
assert_equal(0, result[14])
assert_equal(0, result[15])
assert_equal(0, result[16])
assert_equal(0, result[17])
assert_equal(0, result[18])
assert_equal(0, result[19])
assert_equal(0, result[20])
assert_equal(0, result[21])
assert_equal(0, result[22])
assert_equal(0, result[23])
assert_equal(0, result[24])
assert_equal(0, result[25])
assert_equal(0, result[26])
assert_equal(1, result[27])
assert_equal(0, result[28])
assert_equal(0, result[29])
assert_equal(1, result[30])
assert_equal(nil, result[31])
result = Report::TicketFirstSolution.items(
range_start: '2015-10-01T00:00:00Z',
range_end: '2015-10-31T23:59:59Z',
interval: 'day', # year, quarter, month, week, day, hour, minute, second
selector: {}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(ticket5.id, result[:ticket_ids][0])
assert_equal(ticket6.id, result[:ticket_ids][1])
assert_equal(nil, result[:ticket_ids][2])
# hour
result = Report::TicketFirstSolution.aggs(
range_start: '2015-10-28T00:00:00Z',
range_end: '2015-10-28T23:59:59Z',
interval: 'hour', # year, quarter, month, week, day, hour, minute, second
selector: {}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(0, result[0])
assert_equal(0, result[1])
assert_equal(0, result[2])
assert_equal(0, result[3])
assert_equal(0, result[4])
assert_equal(0, result[5])
assert_equal(0, result[6])
assert_equal(0, result[7])
assert_equal(0, result[8])
assert_equal(0, result[9])
assert_equal(0, result[10])
assert_equal(1, result[11])
assert_equal(0, result[12])
assert_equal(0, result[13])
assert_equal(0, result[14])
assert_equal(0, result[15])
assert_equal(0, result[16])
assert_equal(0, result[17])
assert_equal(0, result[18])
assert_equal(0, result[19])
assert_equal(0, result[20])
assert_equal(0, result[21])
assert_equal(0, result[22])
assert_equal(0, result[23])
assert_equal(nil, result[24])
result = Report::TicketFirstSolution.items(
range_start: '2015-10-28T00:00:00Z',
range_end: '2015-10-28T23:59:59Z',
interval: 'hour', # year, quarter, month, week, day, hour, minute, second
selector: {}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(ticket5.id, result[:ticket_ids][0])
assert_equal(nil, result[:ticket_ids][1])
# created by channel and direction
end
test 'reopen' do
# month
result = Report::TicketReopened.aggs(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
interval: 'month', # year, quarter, month, week, day, hour, minute, second
selector: {}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(0, result[0])
assert_equal(0, result[1])
assert_equal(0, result[2])
assert_equal(0, result[3])
assert_equal(0, result[4])
assert_equal(0, result[5])
assert_equal(0, result[6])
assert_equal(0, result[7])
assert_equal(0, result[8])
assert_equal(1, result[9])
assert_equal(0, result[10])
assert_equal(0, result[11])
assert_equal(nil, result[12])
result = Report::TicketReopened.items(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
selector: {}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(ticket5.id, result[:ticket_ids][0])
assert_equal(nil, result[:ticket_ids][1])
# month - with selector #1
result = Report::TicketReopened.aggs(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
interval: 'month', # year, quarter, month, week, day, hour, minute, second
selector: {
'ticket.priority_id' => {
'operator' => 'is',
'value' => [Ticket::Priority.lookup(name: '3 high').id],
}
}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(0, result[0])
assert_equal(0, result[1])
assert_equal(0, result[2])
assert_equal(0, result[3])
assert_equal(0, result[4])
assert_equal(0, result[5])
assert_equal(0, result[6])
assert_equal(0, result[7])
assert_equal(0, result[8])
assert_equal(1, result[9])
assert_equal(0, result[10])
assert_equal(0, result[11])
assert_equal(nil, result[12])
result = Report::TicketReopened.items(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
selector: {
'ticket.priority_id' => {
'operator' => 'is',
'value' => [Ticket::Priority.lookup(name: '3 high').id],
}
}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(ticket5.id, result[:ticket_ids][0])
assert_equal(nil, result[:ticket_ids][1])
# month - with selector #2
result = Report::TicketReopened.aggs(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
interval: 'month', # year, quarter, month, week, day, hour, minute, second
selector: {
'ticket.priority_id' => {
'operator' => 'is not',
'value' => [Ticket::Priority.lookup(name: '3 high').id],
}
}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(0, result[0])
assert_equal(0, result[1])
assert_equal(0, result[2])
assert_equal(0, result[3])
assert_equal(0, result[4])
assert_equal(0, result[5])
assert_equal(0, result[6])
assert_equal(0, result[7])
assert_equal(0, result[8])
assert_equal(0, result[9])
assert_equal(0, result[10])
assert_equal(0, result[11])
assert_equal(nil, result[12])
result = Report::TicketReopened.items(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
selector: {
'ticket.priority_id' => {
'operator' => 'is not',
'value' => [Ticket::Priority.lookup(name: '3 high').id],
}
}, # ticket selector to get only a collection of tickets
)
assert(result)
assert_equal(nil, result[:ticket_ids][0])
end
test 'move in/out' do
# month
result = Report::TicketMoved.aggs(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
interval: 'month', # year, quarter, month, week, day, hour, minute, second
selector: {
'ticket.group_id' => {
'operator' => 'is',
'value' => [Group.lookup(name: 'Users').id],
}
}, # ticket selector to get only a collection of tickets
params: {
type: 'in',
},
)
assert(result)
assert_equal(0, result[0])
assert_equal(0, result[1])
assert_equal(0, result[2])
assert_equal(0, result[3])
assert_equal(0, result[4])
assert_equal(0, result[5])
assert_equal(0, result[6])
assert_equal(0, result[7])
assert_equal(0, result[8])
assert_equal(1, result[9])
assert_equal(0, result[10])
assert_equal(0, result[11])
assert_equal(nil, result[12])
result = Report::TicketMoved.items(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
selector: {
'ticket.group_id' => {
'operator' => 'is',
'value' => [Group.lookup(name: 'Users').id],
}
}, # ticket selector to get only a collection of tickets
params: {
type: 'in',
},
)
assert(result)
assert_equal(ticket1.id, result[:ticket_ids][0])
assert_equal(nil, result[:ticket_ids][1])
# out
result = Report::TicketMoved.aggs(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
interval: 'month', # year, quarter, month, week, day, hour, minute, second
selector: {
'ticket.group_id' => {
'operator' => 'is',
'value' => [Group.lookup(name: 'Users').id],
}
}, # ticket selector to get only a collection of tickets
params: {
type: 'out',
},
)
assert(result)
assert_equal(0, result[0])
assert_equal(0, result[1])
assert_equal(0, result[2])
assert_equal(0, result[3])
assert_equal(0, result[4])
assert_equal(0, result[5])
assert_equal(0, result[6])
assert_equal(0, result[7])
assert_equal(0, result[8])
assert_equal(1, result[9])
assert_equal(0, result[10])
assert_equal(0, result[11])
assert_equal(nil, result[12])
result = Report::TicketMoved.items(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
selector: {
'ticket.group_id' => {
'operator' => 'is',
'value' => [Group.lookup(name: 'Users').id],
}
}, # ticket selector to get only a collection of tickets
params: {
type: 'out',
},
)
assert(result)
assert_equal(ticket2.id, result[:ticket_ids][0])
assert_equal(nil, result[:ticket_ids][1])
end
test 'created at' do
# month
result = Report::TicketGenericTime.aggs(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
interval: 'month', # year, quarter, month, week, day, hour, minute, second
selector: {}, # ticket selector to get only a collection of tickets
params: { field: 'created_at' },
)
assert(result)
assert_equal(0, result[0])
assert_equal(0, result[1])
assert_equal(0, result[2])
assert_equal(0, result[3])
assert_equal(0, result[4])
assert_equal(0, result[5])
assert_equal(0, result[6])
assert_equal(0, result[7])
assert_equal(0, result[8])
assert_equal(6, result[9])
assert_equal(1, result[10])
assert_equal(0, result[11])
assert_equal(nil, result[12])
result = Report::TicketGenericTime.items(
range_start: '2015-01-01T00:00:00Z',
range_end: '2015-12-31T23:59:59Z',
selector: {}, # ticket selector to get only a collection of tickets
params: { field: 'created_at' },
)
assert(result)
assert_equal(ticket1.id, result[:ticket_ids][0])
assert_equal(ticket2.id, result[:ticket_ids][1])
assert_equal(ticket3.id, result[:ticket_ids][2])
assert_equal(ticket4.id, result[:ticket_ids][3])
assert_equal(ticket5.id, result[:ticket_ids][4])
assert_equal(ticket6.id, result[:ticket_ids][5])
assert_equal(ticket7.id, result[:ticket_ids][6])
assert_equal(nil, result[:ticket_ids][7])
end
end