From 64c86a758f696ab845de65ca875fba635c21706c Mon Sep 17 00:00:00 2001 From: Rolf Schmidt Date: Tue, 18 Apr 2017 09:52:39 +0200 Subject: [PATCH 1/2] Improved code to support admin user with different user login (#767). --- app/models/ticket.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/ticket.rb b/app/models/ticket.rb index c8065361f..863338ca8 100644 --- a/app/models/ticket.rb +++ b/app/models/ticket.rb @@ -311,7 +311,7 @@ returns self.state_id = Ticket::State.lookup(name: 'merged').id # rest owner - self.owner_id = User.find_by(login: '-').id + self.owner_id = 1 # save ticket save! From c5c6876054efabf0a197d3a68aa416d04d06e0d7 Mon Sep 17 00:00:00 2001 From: Rolf Schmidt Date: Tue, 18 Apr 2017 10:06:22 +0200 Subject: [PATCH 2/2] Fixed issue #645 - Dashboard widget "Waiting time today" is not counting. --- .../app/controllers/_dashboard/stats.coffee | 28 ++-- .../app/views/dashboard/stats.jst.eco | 4 +- lib/stats.rb | 1 + lib/stats/ticket_waiting_time.rb | 73 +++++++++ test/unit/stats_ticket_waiting_time_test.rb | 150 ++++++++++++++++++ 5 files changed, 239 insertions(+), 17 deletions(-) create mode 100644 lib/stats/ticket_waiting_time.rb create mode 100644 test/unit/stats_ticket_waiting_time_test.rb diff --git a/app/assets/javascripts/app/controllers/_dashboard/stats.coffee b/app/assets/javascripts/app/controllers/_dashboard/stats.coffee index 7fa9674ba..3a797a52b 100644 --- a/app/assets/javascripts/app/controllers/_dashboard/stats.coffee +++ b/app/assets/javascripts/app/controllers/_dashboard/stats.coffee @@ -12,8 +12,8 @@ class App.DashboardStats extends App.Controller @render() render: (data = {}) -> - if !data.TicketResponseTime - data.TicketResponseTime = + if !data.StatsTicketWaitingTime + data.StatsTicketWaitingTime = handling_time: 0 average: 0 average_per_agent: 0 @@ -60,10 +60,10 @@ class App.DashboardStats extends App.Controller @html App.view('dashboard/stats')(data) - if data.TicketResponseTime - @renderWidgetClockFace data.TicketResponseTime.handling_time + if data.StatsTicketWaitingTime + @renderWidgetClockFace data.StatsTicketWaitingTime.handling_time, data.StatsTicketWaitingTime.state, data.StatsTicketWaitingTime.percent - renderWidgetClockFace: (time, max_time = 60) => + renderWidgetClockFace: (time, state, percent) => canvas = @el.find 'canvas' ctx = canvas.get(0).getContext '2d' radius = 26 @@ -73,17 +73,15 @@ class App.DashboardStats extends App.Controller canvas.attr 'width', 2 * radius canvas.attr 'height', 2 * radius - time = max_time if time > max_time - handlingTimeColors = {} - handlingTimeColors[max_time/12] = '#38AE6A' # supergood - handlingTimeColors[max_time/6] = '#A9AC41' # good - handlingTimeColors[max_time/4] = '#FAAB00' # ok - handlingTimeColors[max_time/3] = '#F6820B' # bad - handlingTimeColors[max_time/2] = '#F35910' # superbad + handlingTimeColors['supergood'] = '#38AE6A' # supergood + handlingTimeColors['good'] = '#A9AC41' # good + handlingTimeColors['ok'] = '#FAAB00' # ok + handlingTimeColors['bad'] = '#F6820B' # bad + handlingTimeColors['superbad'] = '#F35910' # superbad - for handlingTime, timeColor of handlingTimeColors - if time <= handlingTime + for handlingState, timeColor of handlingTimeColors + if state == handlingState backgroundColor = timeColor break @@ -101,7 +99,7 @@ class App.DashboardStats extends App.Controller ctx.beginPath() 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.lineTo radius, radius ctx.closePath() diff --git a/app/assets/javascripts/app/views/dashboard/stats.jst.eco b/app/assets/javascripts/app/views/dashboard/stats.jst.eco index 5312d1477..940f38c1d 100644 --- a/app/assets/javascripts/app/views/dashboard/stats.jst.eco +++ b/app/assets/javascripts/app/views/dashboard/stats.jst.eco @@ -8,8 +8,8 @@
-
<%- @T('My handling time: %s minutes', @TicketResponseTime.handling_time) %>
-
<%- @T('Average: %s minutes', @TicketResponseTime.average_per_agent) %>
+
<%- @T('My handling time: %s minutes', @StatsTicketWaitingTime.handling_time) %>
+
<%- @T('Average: %s minutes', @StatsTicketWaitingTime.average_per_agent) %>
diff --git a/lib/stats.rb b/lib/stats.rb index 0b994953a..7d7eb7e0d 100644 --- a/lib/stats.rb +++ b/lib/stats.rb @@ -24,6 +24,7 @@ returns Stats::TicketLoadMeasure, Stats::TicketEscalation, Stats::TicketReopen, + Stats::TicketWaitingTime, ] # generate stats per agent diff --git a/lib/stats/ticket_waiting_time.rb b/lib/stats/ticket_waiting_time.rb new file mode 100644 index 000000000..7a7bda2a4 --- /dev/null +++ b/lib/stats/ticket_waiting_time.rb @@ -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 diff --git a/test/unit/stats_ticket_waiting_time_test.rb b/test/unit/stats_ticket_waiting_time_test.rb new file mode 100644 index 000000000..d2d72d784 --- /dev/null +++ b/test/unit/stats_ticket_waiting_time_test.rb @@ -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