mirror of
https://github.com/RocketChat/Rocket.Chat.git
synced 2025-12-28 14:58:55 +00:00
fix: First response time livechat metrics are associated with the last agent who served the room (#34156)
Some checks are pending
Deploy GitHub Pages / deploy-preview (push) Waiting to run
CI / ⚙️ Variables Setup (push) Waiting to run
CI / 🚀 Notify external services - draft (push) Blocked by required conditions
CI / 📦 Build Packages (push) Blocked by required conditions
CI / deploy-preview (push) Blocked by required conditions
CI / 📦 Meteor Build - coverage (push) Blocked by required conditions
CI / 📦 Meteor Build - official (push) Blocked by required conditions
CI / Builds matrix rust bindings against alpine (push) Waiting to run
CI / 🚢 Build Docker Images for Testing (alpine) (push) Blocked by required conditions
CI / 🚢 Build Docker Images for Production (alpine) (push) Blocked by required conditions
CI / 🔎 Code Check (push) Blocked by required conditions
CI / 🔨 Test Unit (push) Blocked by required conditions
CI / 🔨 Test API (CE) (push) Blocked by required conditions
CI / 🔨 Test UI (CE) (push) Blocked by required conditions
CI / 🔨 Test API (EE) (push) Blocked by required conditions
CI / 🔨 Test UI (EE) (push) Blocked by required conditions
CI / ✅ Tests Done (push) Blocked by required conditions
CI / 🚀 Publish build assets (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (main) (alpine) (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (services) (account) (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (services) (authorization) (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (services) (ddp-streamer) (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (services) (omnichannel-transcript) (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (services) (presence) (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (services) (queue-worker) (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (services) (stream-hub) (push) Blocked by required conditions
CI / 🚀 Notify external services (push) Blocked by required conditions
CI / trigger-dependent-workflows (push) Blocked by required conditions
CI / Update Version Durability (push) Blocked by required conditions
Code scanning - action / CodeQL-Build (push) Waiting to run
Some checks are pending
Deploy GitHub Pages / deploy-preview (push) Waiting to run
CI / ⚙️ Variables Setup (push) Waiting to run
CI / 🚀 Notify external services - draft (push) Blocked by required conditions
CI / 📦 Build Packages (push) Blocked by required conditions
CI / deploy-preview (push) Blocked by required conditions
CI / 📦 Meteor Build - coverage (push) Blocked by required conditions
CI / 📦 Meteor Build - official (push) Blocked by required conditions
CI / Builds matrix rust bindings against alpine (push) Waiting to run
CI / 🚢 Build Docker Images for Testing (alpine) (push) Blocked by required conditions
CI / 🚢 Build Docker Images for Production (alpine) (push) Blocked by required conditions
CI / 🔎 Code Check (push) Blocked by required conditions
CI / 🔨 Test Unit (push) Blocked by required conditions
CI / 🔨 Test API (CE) (push) Blocked by required conditions
CI / 🔨 Test UI (CE) (push) Blocked by required conditions
CI / 🔨 Test API (EE) (push) Blocked by required conditions
CI / 🔨 Test UI (EE) (push) Blocked by required conditions
CI / ✅ Tests Done (push) Blocked by required conditions
CI / 🚀 Publish build assets (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (main) (alpine) (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (services) (account) (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (services) (authorization) (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (services) (ddp-streamer) (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (services) (omnichannel-transcript) (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (services) (presence) (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (services) (queue-worker) (push) Blocked by required conditions
CI / 🚀 Publish Docker Image (services) (stream-hub) (push) Blocked by required conditions
CI / 🚀 Notify external services (push) Blocked by required conditions
CI / trigger-dependent-workflows (push) Blocked by required conditions
CI / Update Version Durability (push) Blocked by required conditions
Code scanning - action / CodeQL-Build (push) Waiting to run
This commit is contained in:
parent
b32c629765
commit
47f24c2fb7
6
.changeset/fair-carrots-trade.md
Normal file
6
.changeset/fair-carrots-trade.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
"@rocket.chat/meteor": patch
|
||||
"@rocket.chat/model-typings": patch
|
||||
---
|
||||
|
||||
Fixes "Average first response time" and "Best first response time" metrics being associated with the last agent who served the room (instead of the first one)
|
||||
@ -2147,7 +2147,7 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
|
||||
};
|
||||
|
||||
return this.find(query, {
|
||||
projection: { ts: 1, departmentId: 1, open: 1, servedBy: 1, metrics: 1, msgs: 1 },
|
||||
projection: { ts: 1, departmentId: 1, open: 1, servedBy: 1, responseBy: 1, metrics: 1, msgs: 1 },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
});
|
||||
@ -267,7 +267,7 @@ export class AgentOverviewData {
|
||||
}
|
||||
|
||||
async Best_first_response_time(from: moment.Moment, to: moment.Moment, departmentId?: string, extraQuery: Filter<IOmnichannelRoom> = {}) {
|
||||
const agentFirstRespTime = new Map(); // stores avg response time for each agent
|
||||
const agentFirstRespTime = new Map(); // stores best response time for each agent
|
||||
const date = {
|
||||
gte: from.toDate(),
|
||||
lte: to.toDate(),
|
||||
@ -285,12 +285,12 @@ export class AgentOverviewData {
|
||||
data: [],
|
||||
};
|
||||
|
||||
await this.roomsModel.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics, servedBy }) => {
|
||||
if (servedBy && metrics && metrics.response && metrics.response.ft) {
|
||||
if (agentFirstRespTime.has(servedBy.username)) {
|
||||
agentFirstRespTime.set(servedBy.username, Math.min(agentFirstRespTime.get(servedBy.username), metrics.response.ft));
|
||||
await this.roomsModel.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics, responseBy }) => {
|
||||
if (responseBy && metrics && metrics.response && metrics.response.ft) {
|
||||
if (agentFirstRespTime.has(responseBy.username)) {
|
||||
agentFirstRespTime.set(responseBy.username, Math.min(agentFirstRespTime.get(responseBy.username), metrics.response.ft));
|
||||
} else {
|
||||
agentFirstRespTime.set(servedBy.username, metrics.response.ft);
|
||||
agentFirstRespTime.set(responseBy.username, metrics.response.ft);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -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,62 @@ 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);
|
||||
});
|
||||
|
||||
await sendAgentMessage(roomId, 'first response from forwarded agent', agent.credentials);
|
||||
|
||||
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;
|
||||
@ -1019,14 +1075,16 @@ describe('LIVECHAT - dashboards', function () {
|
||||
|
||||
describe('[livechat/analytics/agent-overview] - Best first response time', () => {
|
||||
let agent: { credentials: Credentials; user: IUser & { username: string } };
|
||||
let forwardAgent: { credentials: Credentials; user: IUser & { username: string } };
|
||||
let originalBestFirstResponseTimeInSeconds: number;
|
||||
let roomId: string;
|
||||
|
||||
before(async () => {
|
||||
agent = await createAnOnlineAgent();
|
||||
forwardAgent = await createAnOnlineAgent();
|
||||
});
|
||||
|
||||
after(() => deleteUser(agent.user));
|
||||
after(() => Promise.all([deleteUser(agent.user), deleteUser(forwardAgent.user)]));
|
||||
|
||||
it('should return no best response time for an agent if no response has been sent in the period', async () => {
|
||||
await startANewLivechatRoomAndTakeIt({ agent: agent.credentials });
|
||||
@ -1110,6 +1168,62 @@ describe('LIVECHAT - dashboards', function () {
|
||||
const bestFirstResponseTimeInSeconds = moment.duration(agentData.value).asSeconds();
|
||||
expect(bestFirstResponseTimeInSeconds).to.be.equal(originalBestFirstResponseTimeInSeconds);
|
||||
});
|
||||
|
||||
it('should correctly associate best 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);
|
||||
});
|
||||
|
||||
await sendAgentMessage(roomId, 'first response from forwarded agent', agent.credentials);
|
||||
|
||||
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: 'Best_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 best 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 bestFirstResponseTimeInSeconds = moment.duration(agentData.value).asSeconds();
|
||||
expect(bestFirstResponseTimeInSeconds).to.be.equal(originalBestFirstResponseTimeInSeconds);
|
||||
|
||||
// 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 forwardAgentBestFirstResponseTimeInSeconds = moment.duration(forwardAgentData.value).asSeconds();
|
||||
expect(forwardAgentBestFirstResponseTimeInSeconds).to.be.lessThan(originalBestFirstResponseTimeInSeconds);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/analytics/overview', () => {
|
||||
@ -1170,12 +1284,12 @@ describe('LIVECHAT - dashboards', function () {
|
||||
expect(result.body).to.be.an('array');
|
||||
|
||||
const expectedResult = [
|
||||
{ title: 'Total_conversations', value: 13 },
|
||||
{ title: 'Open_conversations', value: 10 },
|
||||
{ title: 'Total_conversations', value: 15 },
|
||||
{ title: 'Open_conversations', value: 12 },
|
||||
{ title: 'On_Hold_conversations', value: 1 },
|
||||
// { title: 'Total_messages', value: 6 },
|
||||
// { title: 'Busiest_day', value: moment().format('dddd') },
|
||||
{ title: 'Conversations_per_day', value: '6.50' },
|
||||
{ title: 'Conversations_per_day', value: '7.50' },
|
||||
// { title: 'Busiest_time', value: '' },
|
||||
];
|
||||
|
||||
|
||||
@ -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: {
|
||||
@ -818,12 +818,15 @@ describe('AgentData Analytics', () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
it('should calculate correctly when agents have multiple conversations', async () => {
|
||||
it('should associate average first response time with the agent who first responded to the room', async () => {
|
||||
const modelMock = {
|
||||
getAnalyticsMetricsBetweenDate(_params: ILivechatRoomsModel['getAnalyticsMetricsBetweenDate']) {
|
||||
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,106 @@ describe('AgentData Analytics', () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
it('should ignore conversations not being served by any agent', async () => {
|
||||
it('should calculate correctly when agents have multiple conversations', async () => {
|
||||
const modelMock = {
|
||||
getAnalyticsMetricsBetweenDate(_params: ILivechatRoomsModel['getAnalyticsMetricsBetweenDate']) {
|
||||
return [
|
||||
{
|
||||
servedBy: undefined,
|
||||
responseBy: {
|
||||
username: 'agent 1',
|
||||
},
|
||||
metrics: {
|
||||
response: {
|
||||
ft: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
responseBy: {
|
||||
username: 'agent 2',
|
||||
},
|
||||
metrics: {
|
||||
response: {
|
||||
ft: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
responseBy: {
|
||||
username: 'agent 1',
|
||||
},
|
||||
metrics: {
|
||||
response: {
|
||||
ft: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
const agentOverview = new AgentOverviewData(modelMock as any);
|
||||
|
||||
const result = await agentOverview.Avg_first_response_time(moment(), moment(), 'departmentId');
|
||||
|
||||
expect(result).to.be.deep.equal({
|
||||
data: [
|
||||
{
|
||||
name: 'agent 1',
|
||||
value: '00:02:30',
|
||||
},
|
||||
{
|
||||
name: 'agent 2',
|
||||
value: '00:03:20',
|
||||
},
|
||||
],
|
||||
head: [
|
||||
{
|
||||
name: 'Agent',
|
||||
},
|
||||
{ name: 'Avg_first_response_time' },
|
||||
],
|
||||
});
|
||||
});
|
||||
it('should ignore conversations not responded by any agent', async () => {
|
||||
const modelMock = {
|
||||
getAnalyticsMetricsBetweenDate(_params: ILivechatRoomsModel['getAnalyticsMetricsBetweenDate']) {
|
||||
return [
|
||||
{
|
||||
responseBy: undefined,
|
||||
metrics: {
|
||||
response: {
|
||||
ft: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
const agentOverview = new AgentOverviewData(modelMock as any);
|
||||
|
||||
const result = await agentOverview.Avg_first_response_time(moment(), moment(), 'departmentId');
|
||||
|
||||
expect(result).to.be.deep.equal({
|
||||
data: [],
|
||||
head: [
|
||||
{
|
||||
name: 'Agent',
|
||||
},
|
||||
{ name: 'Avg_first_response_time' },
|
||||
],
|
||||
});
|
||||
});
|
||||
it('should ignore conversations served, but not responded by any agent', async () => {
|
||||
const modelMock = {
|
||||
getAnalyticsMetricsBetweenDate(_params: ILivechatRoomsModel['getAnalyticsMetricsBetweenDate']) {
|
||||
return [
|
||||
{
|
||||
servedBy: {
|
||||
username: 'agent 1',
|
||||
},
|
||||
responseBy: undefined,
|
||||
metrics: {
|
||||
response: {
|
||||
ft: 100,
|
||||
@ -914,7 +1017,7 @@ describe('AgentData Analytics', () => {
|
||||
getAnalyticsMetricsBetweenDate(_params: ILivechatRoomsModel['getAnalyticsMetricsBetweenDate']) {
|
||||
return [
|
||||
{
|
||||
servedBy: {
|
||||
responseBy: {
|
||||
username: 'agent 1',
|
||||
},
|
||||
metrics: undefined,
|
||||
@ -966,7 +1069,7 @@ describe('AgentData Analytics', () => {
|
||||
getAnalyticsMetricsBetweenDate(_params: ILivechatRoomsModel['getAnalyticsMetricsBetweenDate']) {
|
||||
return [
|
||||
{
|
||||
servedBy: {
|
||||
responseBy: {
|
||||
username: 'agent 1',
|
||||
},
|
||||
metrics: {
|
||||
@ -976,7 +1079,7 @@ describe('AgentData Analytics', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
servedBy: {
|
||||
responseBy: {
|
||||
username: 'agent 2',
|
||||
},
|
||||
metrics: {
|
||||
@ -986,7 +1089,7 @@ describe('AgentData Analytics', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
servedBy: {
|
||||
responseBy: {
|
||||
username: 'agent 3',
|
||||
},
|
||||
metrics: {
|
||||
@ -996,7 +1099,7 @@ describe('AgentData Analytics', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
servedBy: {
|
||||
responseBy: {
|
||||
username: 'agent 4',
|
||||
},
|
||||
metrics: {
|
||||
@ -1006,7 +1109,7 @@ describe('AgentData Analytics', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
servedBy: {
|
||||
responseBy: {
|
||||
username: 'agent 5',
|
||||
},
|
||||
metrics: {
|
||||
@ -1016,9 +1119,116 @@ describe('AgentData Analytics', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
responseBy: {
|
||||
username: 'agent 6',
|
||||
},
|
||||
metrics: {
|
||||
response: {
|
||||
ft: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
const agentOverview = new AgentOverviewData(modelMock as any);
|
||||
|
||||
const result = await agentOverview.Best_first_response_time(moment(), moment(), 'departmentId');
|
||||
|
||||
expect(result).to.be.deep.equal({
|
||||
data: [
|
||||
{ name: 'agent 1', value: '00:01:40' },
|
||||
{ name: 'agent 2', value: '00:03:20' },
|
||||
{ name: 'agent 3', value: '00:00:50' },
|
||||
{ name: 'agent 4', value: '00:02:30' },
|
||||
{ name: 'agent 5', value: '00:04:10' },
|
||||
{ name: 'agent 6', value: '00:05:00' },
|
||||
],
|
||||
head: [
|
||||
{
|
||||
name: 'Agent',
|
||||
},
|
||||
{ name: 'Best_first_response_time' },
|
||||
],
|
||||
});
|
||||
});
|
||||
it('should associate best first response time with the agent who first responded to the room', async () => {
|
||||
const modelMock = {
|
||||
getAnalyticsMetricsBetweenDate(_params: ILivechatRoomsModel['getAnalyticsMetricsBetweenDate']) {
|
||||
return [
|
||||
{
|
||||
responseBy: {
|
||||
username: 'agent 1',
|
||||
},
|
||||
servedBy: {
|
||||
username: 'agent 2',
|
||||
},
|
||||
metrics: {
|
||||
response: {
|
||||
ft: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
responseBy: {
|
||||
username: 'agent 2',
|
||||
},
|
||||
servedBy: {
|
||||
username: 'agent 3',
|
||||
},
|
||||
metrics: {
|
||||
response: {
|
||||
ft: 200,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
responseBy: {
|
||||
username: 'agent 3',
|
||||
},
|
||||
servedBy: {
|
||||
username: 'agent 4',
|
||||
},
|
||||
metrics: {
|
||||
response: {
|
||||
ft: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
responseBy: {
|
||||
username: 'agent 4',
|
||||
},
|
||||
servedBy: {
|
||||
username: 'agent 5',
|
||||
},
|
||||
metrics: {
|
||||
response: {
|
||||
ft: 150,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
responseBy: {
|
||||
username: 'agent 5',
|
||||
},
|
||||
servedBy: {
|
||||
username: 'agent 6',
|
||||
},
|
||||
metrics: {
|
||||
response: {
|
||||
ft: 250,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
responseBy: {
|
||||
username: 'agent 6',
|
||||
},
|
||||
servedBy: {
|
||||
username: 'agent 7',
|
||||
},
|
||||
metrics: {
|
||||
response: {
|
||||
ft: 300,
|
||||
@ -1055,7 +1265,7 @@ describe('AgentData Analytics', () => {
|
||||
getAnalyticsMetricsBetweenDate(_params: ILivechatRoomsModel['getAnalyticsMetricsBetweenDate']) {
|
||||
return [
|
||||
{
|
||||
servedBy: {
|
||||
responseBy: {
|
||||
username: 'agent 1',
|
||||
},
|
||||
metrics: {
|
||||
@ -1065,7 +1275,7 @@ describe('AgentData Analytics', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
servedBy: {
|
||||
responseBy: {
|
||||
username: 'agent 2',
|
||||
},
|
||||
metrics: {
|
||||
@ -1075,7 +1285,7 @@ describe('AgentData Analytics', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
servedBy: {
|
||||
responseBy: {
|
||||
username: 'agent 3',
|
||||
},
|
||||
metrics: {
|
||||
@ -1085,7 +1295,7 @@ describe('AgentData Analytics', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
servedBy: {
|
||||
responseBy: {
|
||||
username: 'agent 4',
|
||||
},
|
||||
metrics: {
|
||||
@ -1095,7 +1305,7 @@ describe('AgentData Analytics', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
servedBy: {
|
||||
responseBy: {
|
||||
username: 'agent 5',
|
||||
},
|
||||
metrics: {
|
||||
@ -1105,7 +1315,7 @@ describe('AgentData Analytics', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
servedBy: {
|
||||
responseBy: {
|
||||
username: 'agent 6',
|
||||
},
|
||||
metrics: {
|
||||
@ -1115,7 +1325,7 @@ describe('AgentData Analytics', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
servedBy: {
|
||||
responseBy: {
|
||||
username: 'agent 1',
|
||||
},
|
||||
metrics: {
|
||||
@ -1149,10 +1359,31 @@ 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, metrics: { response: { ft: 100 } } }];
|
||||
return [{ responseBy: undefined, metrics: { response: { ft: 100 } } }];
|
||||
},
|
||||
};
|
||||
|
||||
const agentOverview = new AgentOverviewData(modelMock as any);
|
||||
|
||||
const result = await agentOverview.Best_first_response_time(moment(), moment(), 'departmentId');
|
||||
|
||||
expect(result).to.be.deep.equal({
|
||||
data: [],
|
||||
head: [
|
||||
{
|
||||
name: 'Agent',
|
||||
},
|
||||
{ name: 'Best_first_response_time' },
|
||||
],
|
||||
});
|
||||
});
|
||||
it('should ignore conversations served, but not responded by any agent', async () => {
|
||||
const modelMock = {
|
||||
getAnalyticsMetricsBetweenDate(_params: ILivechatRoomsModel['getAnalyticsMetricsBetweenDate']) {
|
||||
return [{ servedBy: { username: 'agent1' }, responseBy: undefined, metrics: { response: { ft: 100 } } }];
|
||||
},
|
||||
};
|
||||
|
||||
@ -1173,7 +1404,7 @@ describe('AgentData Analytics', () => {
|
||||
it('should ignore conversations with no metrics', async () => {
|
||||
const modelMock = {
|
||||
getAnalyticsMetricsBetweenDate(_params: ILivechatRoomsModel['getAnalyticsMetricsBetweenDate']) {
|
||||
return [{ servedBy: { username: 'agent 1' }, metrics: undefined }];
|
||||
return [{ responseBy: { username: 'agent 1' }, metrics: undefined }];
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -238,7 +238,7 @@ export interface ILivechatRoomsModel extends IBaseModel<IOmnichannelRoom> {
|
||||
date: { gte: Date; lte: Date },
|
||||
data?: { departmentId?: string },
|
||||
extraQuery?: Filter<IOmnichannelRoom>,
|
||||
): FindCursor<Pick<IOmnichannelRoom, 'ts' | 'departmentId' | 'open' | 'servedBy' | 'metrics' | 'msgs'>>;
|
||||
): FindCursor<Pick<IOmnichannelRoom, 'ts' | 'departmentId' | 'open' | 'servedBy' | 'responseBy' | 'metrics' | 'msgs'>>;
|
||||
getAnalyticsMetricsBetweenDateWithMessages(
|
||||
t: string,
|
||||
date: { gte: Date; lte: Date },
|
||||
|
||||
Loading…
Reference in New Issue
Block a user