From 954f8ea1329ab88809158312fc73f6b44637d904 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Tue, 10 Dec 2024 13:13:00 -0300 Subject: [PATCH] fix: Always attach avg first response time metric to the first agent who responded to the room --- .../omnichannel-analytics/AgentData.ts | 14 ++--- .../end-to-end/api/livechat/04-dashboards.ts | 60 ++++++++++++++++++- .../omnichannel-analytics/AgentData.tests.ts | 21 +++++-- 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/apps/meteor/server/services/omnichannel-analytics/AgentData.ts b/apps/meteor/server/services/omnichannel-analytics/AgentData.ts index 770fea291b46..e92f7c7b6716 100644 --- a/apps/meteor/server/services/omnichannel-analytics/AgentData.ts +++ b/apps/meteor/server/services/omnichannel-analytics/AgentData.ts @@ -235,15 +235,15 @@ export class AgentOverviewData { data: [], }; - await this.roomsModel.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics, servedBy }) => { - if (servedBy && metrics && metrics.response && metrics.response.ft) { - if (agentAvgRespTime.has(servedBy.username)) { - agentAvgRespTime.set(servedBy.username, { - frt: agentAvgRespTime.get(servedBy.username).frt + metrics.response.ft, - total: agentAvgRespTime.get(servedBy.username).total + 1, + await this.roomsModel.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics, responseBy }) => { + if (responseBy && metrics && metrics.response && metrics.response.ft) { + if (agentAvgRespTime.has(responseBy.username)) { + agentAvgRespTime.set(responseBy.username, { + frt: agentAvgRespTime.get(responseBy.username).frt + metrics.response.ft, + total: agentAvgRespTime.get(responseBy.username).total + 1, }); } else { - agentAvgRespTime.set(servedBy.username, { + agentAvgRespTime.set(responseBy.username, { frt: metrics.response.ft, total: 1, }); diff --git a/apps/meteor/tests/end-to-end/api/livechat/04-dashboards.ts b/apps/meteor/tests/end-to-end/api/livechat/04-dashboards.ts index 4c8237c34045..0858d2685f53 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/04-dashboards.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/04-dashboards.ts @@ -922,6 +922,7 @@ describe('LIVECHAT - dashboards', function () { describe('[livechat/analytics/agent-overview] - Average first response time', () => { let agent: { credentials: Credentials; user: IUser & { username: string } }; + let forwardAgent: { credentials: Credentials; user: IUser & { username: string } }; let originalFirstResponseTimeInSeconds: number; let roomId: string; const firstDelayInSeconds = 4; @@ -929,11 +930,10 @@ describe('LIVECHAT - dashboards', function () { before(async () => { agent = await createAnOnlineAgent(); + forwardAgent = await createAnOnlineAgent(); }); - after(async () => { - await deleteUser(agent.user); - }); + after(async () => Promise.all([deleteUser(agent.user), deleteUser(forwardAgent.user)])); it('should return no average response time for an agent if no response has been sent in the period', async () => { await startANewLivechatRoomAndTakeIt({ agent: agent.credentials }); @@ -984,6 +984,60 @@ describe('LIVECHAT - dashboards', function () { expect(originalFirstResponseTimeInSeconds).to.be.greaterThanOrEqual(firstDelayInSeconds); }); + it('should correctly associate the first response time to the first agent who responded the room', async () => { + const response = await startANewLivechatRoomAndTakeIt({ agent: forwardAgent.credentials }); + roomId = response.room._id; + + await sendAgentMessage(roomId, 'first response from agent', forwardAgent.credentials); + + await request + .post(api('livechat/room.forward')) + .set(credentials) + .send({ + roomId, + userId: agent.user._id, + comment: 'test comment', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + }); + + const today = moment().startOf('day').format('YYYY-MM-DD'); + const result = await request + .get(api('livechat/analytics/agent-overview')) + .query({ from: today, to: today, name: 'Avg_first_response_time' }) + .set(credentials) + .expect('Content-Type', 'application/json') + .expect(200); + + expect(result.body).to.have.property('success', true); + expect(result.body).to.have.property('head'); + expect(result.body).to.have.property('data'); + expect(result.body.data).to.be.an('array'); + + // The agent to whom the room has been forwarded shouldn't have their average first response time changed + const agentData = result.body.data.find( + (agentOverviewData: { name: string; value: string }) => agentOverviewData.name === agent.user.username, + ); + expect(agentData).to.not.be.undefined; + expect(agentData).to.have.property('name', agent.user.username); + expect(agentData).to.have.property('value'); + const averageFirstResponseTimeInSeconds = moment.duration(agentData.value).asSeconds(); + expect(originalFirstResponseTimeInSeconds).to.be.equal(averageFirstResponseTimeInSeconds); + + // A room's first response time should be attached to the agent who first responded to it even if it has been forwarded + const forwardAgentData = result.body.data.find( + (agentOverviewData: { name: string; value: string }) => agentOverviewData.name === forwardAgent.user.username, + ); + expect(forwardAgentData).to.not.be.undefined; + expect(forwardAgentData).to.have.property('name', forwardAgent.user.username); + expect(forwardAgentData).to.have.property('value'); + const forwardAgentAverageFirstResponseTimeInSeconds = moment.duration(forwardAgentData.value).asSeconds(); + expect(originalFirstResponseTimeInSeconds).to.be.greaterThan(forwardAgentAverageFirstResponseTimeInSeconds); + }); + it('should correctly calculate the average time of first responses for an agent', async () => { const response = await startANewLivechatRoomAndTakeIt({ agent: agent.credentials }); roomId = response.room._id; diff --git a/apps/meteor/tests/unit/server/services/omnichannel-analytics/AgentData.tests.ts b/apps/meteor/tests/unit/server/services/omnichannel-analytics/AgentData.tests.ts index cdc085a8b87d..b143bad7244a 100644 --- a/apps/meteor/tests/unit/server/services/omnichannel-analytics/AgentData.tests.ts +++ b/apps/meteor/tests/unit/server/services/omnichannel-analytics/AgentData.tests.ts @@ -735,7 +735,7 @@ describe('AgentData Analytics', () => { getAnalyticsMetricsBetweenDate(_params: ILivechatRoomsModel['getAnalyticsMetricsBetweenDate']) { return [ { - servedBy: { + responseBy: { username: 'agent 1', }, metrics: { @@ -772,7 +772,7 @@ describe('AgentData Analytics', () => { getAnalyticsMetricsBetweenDate(_params: ILivechatRoomsModel['getAnalyticsMetricsBetweenDate']) { return [ { - servedBy: { + responseBy: { username: 'agent 1', }, metrics: { @@ -782,7 +782,7 @@ describe('AgentData Analytics', () => { }, }, { - servedBy: { + responseBy: { username: 'agent 2', }, metrics: { @@ -824,6 +824,9 @@ describe('AgentData Analytics', () => { return [ { servedBy: { + username: 'agent 3', + }, + responseBy: { username: 'agent 1', }, metrics: { @@ -834,6 +837,9 @@ describe('AgentData Analytics', () => { }, { servedBy: { + username: 'agent 4', + }, + responseBy: { username: 'agent 2', }, metrics: { @@ -844,6 +850,9 @@ describe('AgentData Analytics', () => { }, { servedBy: { + username: 'agent 5', + }, + responseBy: { username: 'agent 1', }, metrics: { @@ -879,12 +888,12 @@ describe('AgentData Analytics', () => { ], }); }); - it('should ignore conversations not being served by any agent', async () => { + it('should ignore conversations not responded by any agent', async () => { const modelMock = { getAnalyticsMetricsBetweenDate(_params: ILivechatRoomsModel['getAnalyticsMetricsBetweenDate']) { return [ { - servedBy: undefined, + responseBy: undefined, metrics: { response: { ft: 100, @@ -914,7 +923,7 @@ describe('AgentData Analytics', () => { getAnalyticsMetricsBetweenDate(_params: ILivechatRoomsModel['getAnalyticsMetricsBetweenDate']) { return [ { - servedBy: { + responseBy: { username: 'agent 1', }, metrics: undefined,