Fixed issue #645 - Dashboard widget "Waiting time today" is not counting.
This commit is contained in:
parent
64c86a758f
commit
c5c6876054
5 changed files with 239 additions and 17 deletions
|
@ -12,8 +12,8 @@ class App.DashboardStats extends App.Controller
|
||||||
@render()
|
@render()
|
||||||
|
|
||||||
render: (data = {}) ->
|
render: (data = {}) ->
|
||||||
if !data.TicketResponseTime
|
if !data.StatsTicketWaitingTime
|
||||||
data.TicketResponseTime =
|
data.StatsTicketWaitingTime =
|
||||||
handling_time: 0
|
handling_time: 0
|
||||||
average: 0
|
average: 0
|
||||||
average_per_agent: 0
|
average_per_agent: 0
|
||||||
|
@ -60,10 +60,10 @@ class App.DashboardStats extends App.Controller
|
||||||
|
|
||||||
@html App.view('dashboard/stats')(data)
|
@html App.view('dashboard/stats')(data)
|
||||||
|
|
||||||
if data.TicketResponseTime
|
if data.StatsTicketWaitingTime
|
||||||
@renderWidgetClockFace data.TicketResponseTime.handling_time
|
@renderWidgetClockFace data.StatsTicketWaitingTime.handling_time, data.StatsTicketWaitingTime.state, data.StatsTicketWaitingTime.percent
|
||||||
|
|
||||||
renderWidgetClockFace: (time, max_time = 60) =>
|
renderWidgetClockFace: (time, state, percent) =>
|
||||||
canvas = @el.find 'canvas'
|
canvas = @el.find 'canvas'
|
||||||
ctx = canvas.get(0).getContext '2d'
|
ctx = canvas.get(0).getContext '2d'
|
||||||
radius = 26
|
radius = 26
|
||||||
|
@ -73,17 +73,15 @@ class App.DashboardStats extends App.Controller
|
||||||
canvas.attr 'width', 2 * radius
|
canvas.attr 'width', 2 * radius
|
||||||
canvas.attr 'height', 2 * radius
|
canvas.attr 'height', 2 * radius
|
||||||
|
|
||||||
time = max_time if time > max_time
|
|
||||||
|
|
||||||
handlingTimeColors = {}
|
handlingTimeColors = {}
|
||||||
handlingTimeColors[max_time/12] = '#38AE6A' # supergood
|
handlingTimeColors['supergood'] = '#38AE6A' # supergood
|
||||||
handlingTimeColors[max_time/6] = '#A9AC41' # good
|
handlingTimeColors['good'] = '#A9AC41' # good
|
||||||
handlingTimeColors[max_time/4] = '#FAAB00' # ok
|
handlingTimeColors['ok'] = '#FAAB00' # ok
|
||||||
handlingTimeColors[max_time/3] = '#F6820B' # bad
|
handlingTimeColors['bad'] = '#F6820B' # bad
|
||||||
handlingTimeColors[max_time/2] = '#F35910' # superbad
|
handlingTimeColors['superbad'] = '#F35910' # superbad
|
||||||
|
|
||||||
for handlingTime, timeColor of handlingTimeColors
|
for handlingState, timeColor of handlingTimeColors
|
||||||
if time <= handlingTime
|
if state == handlingState
|
||||||
backgroundColor = timeColor
|
backgroundColor = timeColor
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -101,7 +99,7 @@ class App.DashboardStats extends App.Controller
|
||||||
|
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.moveTo radius, radius
|
ctx.moveTo radius, radius
|
||||||
arcsector = Math.PI * 2 * time/max_time
|
arcsector = Math.PI * 2 * percent
|
||||||
ctx.arc radius, radius, radius, -Math.PI/2, arcsector - Math.PI/2, false
|
ctx.arc radius, radius, radius, -Math.PI/2, arcsector - Math.PI/2, false
|
||||||
ctx.lineTo radius, radius
|
ctx.lineTo radius, radius
|
||||||
ctx.closePath()
|
ctx.closePath()
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
<div class="stat-amount"></div>
|
<div class="stat-amount"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-label"><%- @T('My handling time: %s minutes', @TicketResponseTime.handling_time) %></div>
|
<div class="stat-label"><%- @T('My handling time: %s minutes', @StatsTicketWaitingTime.handling_time) %></div>
|
||||||
<div class="stat-detail"><%- @T('Average: %s minutes', @TicketResponseTime.average_per_agent) %></div>
|
<div class="stat-detail"><%- @T('Average: %s minutes', @StatsTicketWaitingTime.average_per_agent) %></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
|
|
|
@ -24,6 +24,7 @@ returns
|
||||||
Stats::TicketLoadMeasure,
|
Stats::TicketLoadMeasure,
|
||||||
Stats::TicketEscalation,
|
Stats::TicketEscalation,
|
||||||
Stats::TicketReopen,
|
Stats::TicketReopen,
|
||||||
|
Stats::TicketWaitingTime,
|
||||||
]
|
]
|
||||||
|
|
||||||
# generate stats per agent
|
# generate stats per agent
|
||||||
|
|
73
lib/stats/ticket_waiting_time.rb
Normal file
73
lib/stats/ticket_waiting_time.rb
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
class Stats::TicketWaitingTime
|
||||||
|
|
||||||
|
def self.generate(user)
|
||||||
|
|
||||||
|
open_state_ids = Ticket::State.by_category(:open).pluck(:id)
|
||||||
|
|
||||||
|
# get users groups
|
||||||
|
group_ids = user.groups.map(&:id)
|
||||||
|
|
||||||
|
own_waiting = Ticket.where(
|
||||||
|
'owner_id = ? AND group_id IN (?) AND state_id IN (?) AND updated_at > ?', user.id, group_ids, open_state_ids, Time.zone.today
|
||||||
|
)
|
||||||
|
all_waiting = Ticket.where(
|
||||||
|
'group_id IN (?) AND state_id IN (?) AND updated_at > ?', group_ids, open_state_ids, Time.zone.today
|
||||||
|
)
|
||||||
|
|
||||||
|
handling_time = calculate_average(own_waiting, Time.zone.today)
|
||||||
|
if handling_time.positive?
|
||||||
|
handling_time = (handling_time / 60).round
|
||||||
|
end
|
||||||
|
average_per_agent = calculate_average(all_waiting, Time.zone.today)
|
||||||
|
if average_per_agent.positive?
|
||||||
|
average_per_agent = (average_per_agent / 60).round
|
||||||
|
end
|
||||||
|
|
||||||
|
state = 'supergood'
|
||||||
|
percent = 0
|
||||||
|
state = if handling_time <= 60
|
||||||
|
percent = handling_time.to_f / 60
|
||||||
|
'supergood'
|
||||||
|
elsif handling_time <= 60 * 4
|
||||||
|
percent = (handling_time.to_f - 60) / (60 * 3)
|
||||||
|
'good'
|
||||||
|
elsif handling_time <= 60 * 8
|
||||||
|
percent = (handling_time.to_f - 60 * 4) / (60 * 4)
|
||||||
|
'ok'
|
||||||
|
else
|
||||||
|
percent = 1.00
|
||||||
|
'bad'
|
||||||
|
end
|
||||||
|
|
||||||
|
{
|
||||||
|
handling_time: handling_time,
|
||||||
|
average_per_agent: average_per_agent,
|
||||||
|
state: state,
|
||||||
|
percent: percent,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.average_state(result, _user_id)
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.calculate_average(tickets, start_time)
|
||||||
|
average_time = 0
|
||||||
|
count_time = 0
|
||||||
|
|
||||||
|
tickets.each { |ticket|
|
||||||
|
ticket.articles.joins(:type).where('ticket_articles.created_at > ? AND ticket_articles.internal = ? AND ticket_article_types.communication = ?', start_time, false, true).each { |article|
|
||||||
|
if article.sender.name == 'Customer'
|
||||||
|
count_time = article.created_at.to_i
|
||||||
|
else
|
||||||
|
average_time += article.created_at.to_i - count_time
|
||||||
|
count_time = 0
|
||||||
|
end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
average_time
|
||||||
|
end
|
||||||
|
end
|
150
test/unit/stats_ticket_waiting_time_test.rb
Normal file
150
test/unit/stats_ticket_waiting_time_test.rb
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
# encoding: utf-8
|
||||||
|
require 'test_helper'
|
||||||
|
require 'stats/ticket_waiting_time'
|
||||||
|
|
||||||
|
class StatsTicketWaitingTimeTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
|
test 'single ticket' do
|
||||||
|
ticket1 = Ticket.create(
|
||||||
|
title: 'com test 1',
|
||||||
|
group: Group.lookup(name: 'Users'),
|
||||||
|
customer_id: 2,
|
||||||
|
state: Ticket::State.lookup(name: 'new'),
|
||||||
|
priority: Ticket::Priority.lookup(name: '2 normal'),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
# communication 1: waiting time 2 hours (BUT too old yesterday)
|
||||||
|
Ticket::Article.create(
|
||||||
|
ticket_id: ticket1.id,
|
||||||
|
from: 'a@example.com',
|
||||||
|
to: 'a@example.com',
|
||||||
|
subject: 'com test 1',
|
||||||
|
message_id: 'some@id_com_1',
|
||||||
|
body: 'some message 123',
|
||||||
|
internal: false,
|
||||||
|
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
|
||||||
|
type: Ticket::Article::Type.find_by(name: 'email'),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
created_at: '2017-04-12 08:00',
|
||||||
|
updated_at: '2017-04-12 08:00',
|
||||||
|
)
|
||||||
|
Ticket::Article.create(
|
||||||
|
ticket_id: ticket1.id,
|
||||||
|
from: 'a@example.com',
|
||||||
|
to: 'a@example.com',
|
||||||
|
subject: 'com test 1',
|
||||||
|
message_id: 'some@id_com_1',
|
||||||
|
body: 'some message 123',
|
||||||
|
internal: false,
|
||||||
|
sender: Ticket::Article::Sender.find_by(name: 'Agent'),
|
||||||
|
type: Ticket::Article::Type.find_by(name: 'email'),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
created_at: '2017-04-12 10:00',
|
||||||
|
updated_at: '2017-04-12 10:00',
|
||||||
|
)
|
||||||
|
|
||||||
|
# communication 2: waiting time 2 hours
|
||||||
|
Ticket::Article.create(
|
||||||
|
ticket_id: ticket1.id,
|
||||||
|
from: 'a@example.com',
|
||||||
|
to: 'a@example.com',
|
||||||
|
subject: 'com test 1',
|
||||||
|
message_id: 'some@id_com_1',
|
||||||
|
body: 'some message 123',
|
||||||
|
internal: false,
|
||||||
|
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
|
||||||
|
type: Ticket::Article::Type.find_by(name: 'email'),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
created_at: '2017-04-13 08:00',
|
||||||
|
updated_at: '2017-04-13 08:00',
|
||||||
|
)
|
||||||
|
Ticket::Article.create(
|
||||||
|
ticket_id: ticket1.id,
|
||||||
|
from: 'a@example.com',
|
||||||
|
to: 'a@example.com',
|
||||||
|
subject: 'com test 1',
|
||||||
|
message_id: 'some@id_com_1',
|
||||||
|
body: 'some message 123',
|
||||||
|
internal: false,
|
||||||
|
sender: Ticket::Article::Sender.find_by(name: 'Agent'),
|
||||||
|
type: Ticket::Article::Type.find_by(name: 'email'),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
created_at: '2017-04-13 10:00',
|
||||||
|
updated_at: '2017-04-13 10:00',
|
||||||
|
)
|
||||||
|
|
||||||
|
# communication 3: waiting time 4 hours
|
||||||
|
Ticket::Article.create(
|
||||||
|
ticket_id: ticket1.id,
|
||||||
|
from: 'a@example.com',
|
||||||
|
to: 'a@example.com',
|
||||||
|
subject: 'com test 1',
|
||||||
|
message_id: 'some@id_com_1',
|
||||||
|
body: 'some message 123',
|
||||||
|
internal: false,
|
||||||
|
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
|
||||||
|
type: Ticket::Article::Type.find_by(name: 'email'),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
created_at: '2017-04-13 11:00',
|
||||||
|
updated_at: '2017-04-13 11:00',
|
||||||
|
)
|
||||||
|
Ticket::Article.create(
|
||||||
|
ticket_id: ticket1.id,
|
||||||
|
from: 'a@example.com',
|
||||||
|
to: 'a@example.com',
|
||||||
|
subject: 'com test 1',
|
||||||
|
message_id: 'some@id_com_1',
|
||||||
|
body: 'some message 123',
|
||||||
|
internal: false,
|
||||||
|
sender: Ticket::Article::Sender.find_by(name: 'System'),
|
||||||
|
type: Ticket::Article::Type.find_by(name: 'email'),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
created_at: '2017-04-13 15:00',
|
||||||
|
updated_at: '2017-04-13 15:00',
|
||||||
|
)
|
||||||
|
|
||||||
|
# communication 4: INVALID waiting time 1 hour (because internal)
|
||||||
|
Ticket::Article.create(
|
||||||
|
ticket_id: ticket1.id,
|
||||||
|
from: 'a@example.com',
|
||||||
|
to: 'a@example.com',
|
||||||
|
subject: 'com test 1',
|
||||||
|
message_id: 'some@id_com_1',
|
||||||
|
body: 'some message 123',
|
||||||
|
internal: true,
|
||||||
|
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
|
||||||
|
type: Ticket::Article::Type.find_by(name: 'email'),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
created_at: '2017-04-13 15:00',
|
||||||
|
updated_at: '2017-04-13 15:00',
|
||||||
|
)
|
||||||
|
Ticket::Article.create(
|
||||||
|
ticket_id: ticket1.id,
|
||||||
|
from: 'a@example.com',
|
||||||
|
to: 'a@example.com',
|
||||||
|
subject: 'com test 1',
|
||||||
|
message_id: 'some@id_com_1',
|
||||||
|
body: 'some message 123',
|
||||||
|
internal: true,
|
||||||
|
sender: Ticket::Article::Sender.find_by(name: 'System'),
|
||||||
|
type: Ticket::Article::Type.find_by(name: 'email'),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
created_at: '2017-04-13 15:10',
|
||||||
|
updated_at: '2017-04-13 15:10',
|
||||||
|
)
|
||||||
|
|
||||||
|
average_time = Stats::TicketWaitingTime.calculate_average([ticket1, ticket1], '2017-04-13 00:00:00')
|
||||||
|
assert_equal(60 * 60 * 6 * 2, average_time)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in a new issue