snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
72
apps/meteor/server/lib/omnichannel/analytics/agents.ts
Normal file
72
apps/meteor/server/lib/omnichannel/analytics/agents.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { LivechatRooms, LivechatAgentActivity } from '@rocket.chat/models';
|
||||
|
||||
type Params = {
|
||||
start: Date;
|
||||
end: Date;
|
||||
options?: any;
|
||||
};
|
||||
|
||||
export const findAllAverageServiceTimeAsync = async ({ start, end, options = {} }: Params) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const total = await LivechatRooms.findAllAverageServiceTimeByAgents({
|
||||
start,
|
||||
end,
|
||||
onlyCount: true,
|
||||
}).toArray();
|
||||
return {
|
||||
agents: await LivechatRooms.findAllAverageServiceTimeByAgents({
|
||||
start,
|
||||
end,
|
||||
options,
|
||||
}).toArray(),
|
||||
total: total.length ? total[0].total : 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const findAllServiceTimeAsync = async ({ start, end, options = {} }: Params) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const total = await LivechatRooms.findAllServiceTimeByAgent({
|
||||
start,
|
||||
end,
|
||||
onlyCount: true,
|
||||
}).toArray();
|
||||
return {
|
||||
agents: await LivechatRooms.findAllServiceTimeByAgent({ start, end, options }).toArray(),
|
||||
total: total.length ? total[0].total : 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const findAvailableServiceTimeHistoryAsync = async ({
|
||||
start,
|
||||
end,
|
||||
fullReport,
|
||||
options = {},
|
||||
}: {
|
||||
start: string;
|
||||
end: string;
|
||||
fullReport: boolean;
|
||||
options: { sort?: Record<string, number>; offset?: number; count?: number };
|
||||
}) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const total = await LivechatAgentActivity.findAvailableServiceTimeHistory({
|
||||
start,
|
||||
end,
|
||||
fullReport,
|
||||
onlyCount: true,
|
||||
}).toArray();
|
||||
return {
|
||||
agents: await LivechatAgentActivity.findAvailableServiceTimeHistory({
|
||||
start,
|
||||
end,
|
||||
fullReport,
|
||||
options,
|
||||
}).toArray(),
|
||||
total: total.length ? total[0].total : 0,
|
||||
};
|
||||
};
|
||||
395
apps/meteor/server/lib/omnichannel/analytics/dashboards.ts
Normal file
395
apps/meteor/server/lib/omnichannel/analytics/dashboards.ts
Normal file
@@ -0,0 +1,395 @@
|
||||
import { OmnichannelAnalytics } from '@rocket.chat/core-services';
|
||||
import type { IUser } from '@rocket.chat/core-typings';
|
||||
import { LivechatRooms, Users, LivechatVisitors, LivechatAgentActivity } from '@rocket.chat/models';
|
||||
import mem from 'mem';
|
||||
import moment from 'moment';
|
||||
|
||||
import { secondsToHHMMSS } from '../../../../lib/utils/secondsToHHMMSS';
|
||||
import { settings } from '../../../settings';
|
||||
import { getAnalyticsOverviewDataCachedForRealtime } from '../AnalyticsTyped';
|
||||
import {
|
||||
findPercentageOfAbandonedRoomsAsync,
|
||||
findAllAverageOfChatDurationTimeAsync,
|
||||
findAllAverageWaitingTimeAsync,
|
||||
findAllNumberOfAbandonedRoomsAsync,
|
||||
findAllAverageServiceTimeAsync,
|
||||
} from './departments';
|
||||
|
||||
const findAllChatsStatusAsync = async ({ start, end, departmentId = undefined }: { start: Date; end: Date; departmentId?: string }) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
return {
|
||||
open: await LivechatRooms.countAllOpenChatsBetweenDate({ start, end, departmentId }),
|
||||
closed: await LivechatRooms.countAllClosedChatsBetweenDate({ start, end, departmentId }),
|
||||
queued: await LivechatRooms.countAllQueuedChatsBetweenDate({ start, end, departmentId }),
|
||||
onhold: await LivechatRooms.getOnHoldConversationsBetweenDate(start, end, departmentId),
|
||||
};
|
||||
};
|
||||
|
||||
const getProductivityMetricsAsync = async ({
|
||||
start,
|
||||
end,
|
||||
departmentId = undefined,
|
||||
user,
|
||||
}: {
|
||||
start: string;
|
||||
end: string;
|
||||
departmentId?: string;
|
||||
user: Pick<IUser, '_id' | 'utcOffset' | 'language'>;
|
||||
}) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const totalizers =
|
||||
(await OmnichannelAnalytics.getAnalyticsOverviewData({
|
||||
daterange: {
|
||||
from: start,
|
||||
to: end,
|
||||
},
|
||||
analyticsOptions: {
|
||||
name: 'Productivity',
|
||||
},
|
||||
departmentId,
|
||||
utcOffset: user?.utcOffset,
|
||||
language: user?.language || settings.get('Language') || 'en',
|
||||
executedBy: user._id,
|
||||
})) || [];
|
||||
const averageWaitingTime = await findAllAverageWaitingTimeAsync({
|
||||
start: new Date(start),
|
||||
end: new Date(end),
|
||||
departmentId,
|
||||
});
|
||||
|
||||
const totalOfWaitingTime = averageWaitingTime.departments.length;
|
||||
|
||||
const sumOfWaitingTime = averageWaitingTime.departments.reduce((acc: number, serviceTime: { averageWaitingTimeInSeconds: number }) => {
|
||||
acc += serviceTime.averageWaitingTimeInSeconds;
|
||||
return acc;
|
||||
}, 0);
|
||||
const totalOfAvarageWaitingTime = totalOfWaitingTime === 0 ? 0 : sumOfWaitingTime / totalOfWaitingTime;
|
||||
|
||||
return {
|
||||
totalizers: [...totalizers, { title: 'Avg_of_waiting_time', value: secondsToHHMMSS(totalOfAvarageWaitingTime) }],
|
||||
};
|
||||
};
|
||||
|
||||
const getAgentsProductivityMetricsAsync = async ({
|
||||
start,
|
||||
end,
|
||||
departmentId = undefined,
|
||||
user,
|
||||
}: {
|
||||
start: string;
|
||||
end: string;
|
||||
departmentId?: string;
|
||||
user: Pick<IUser, '_id' | 'utcOffset' | 'language'>;
|
||||
}) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
// TODO: check type of date
|
||||
const averageOfAvailableServiceTime = (
|
||||
await LivechatAgentActivity.findAllAverageAvailableServiceTime({
|
||||
date: parseInt(moment(start).format('YYYYMMDD')) as any,
|
||||
departmentId,
|
||||
})
|
||||
)[0];
|
||||
const averageOfServiceTime = await findAllAverageServiceTimeAsync({
|
||||
start: new Date(start),
|
||||
end: new Date(end),
|
||||
departmentId,
|
||||
});
|
||||
const totalizers =
|
||||
(await OmnichannelAnalytics.getAnalyticsOverviewData({
|
||||
daterange: {
|
||||
from: start,
|
||||
to: end,
|
||||
},
|
||||
analyticsOptions: {
|
||||
name: 'Conversations',
|
||||
},
|
||||
departmentId,
|
||||
utcOffset: user.utcOffset,
|
||||
language: user.language || settings.get('Language') || 'en',
|
||||
executedBy: user._id,
|
||||
})) || [];
|
||||
|
||||
const totalOfServiceTime = averageOfServiceTime.departments.length;
|
||||
|
||||
const sumOfServiceTime = averageOfServiceTime.departments.reduce(
|
||||
(
|
||||
acc: number,
|
||||
serviceTime: {
|
||||
averageServiceTimeInSeconds: number;
|
||||
},
|
||||
) => {
|
||||
acc += serviceTime.averageServiceTimeInSeconds;
|
||||
return acc;
|
||||
},
|
||||
0,
|
||||
);
|
||||
const totalOfAverageAvailableServiceTime = averageOfAvailableServiceTime
|
||||
? averageOfAvailableServiceTime.averageAvailableServiceTimeInSeconds
|
||||
: 0;
|
||||
const totalOfAverageServiceTime = totalOfServiceTime === 0 ? 0 : sumOfServiceTime / totalOfServiceTime;
|
||||
|
||||
return {
|
||||
totalizers: [
|
||||
...totalizers.filter((metric: { title: string }) => metric.title === 'Busiest_time'),
|
||||
{
|
||||
title: 'Avg_of_available_service_time',
|
||||
value: secondsToHHMMSS(totalOfAverageAvailableServiceTime),
|
||||
},
|
||||
{ title: 'Avg_of_service_time', value: secondsToHHMMSS(totalOfAverageServiceTime) },
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const getChatsMetricsAsync = async ({ start, end, departmentId = undefined }: { start: Date; end: Date; departmentId?: string }) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const abandonedRooms = await findAllNumberOfAbandonedRoomsAsync({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
});
|
||||
const averageOfAbandonedRooms = await findPercentageOfAbandonedRoomsAsync({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
});
|
||||
const averageOfChatDurationTime = await findAllAverageOfChatDurationTimeAsync({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
});
|
||||
|
||||
const totalOfAbandonedRooms = averageOfAbandonedRooms.departments.length;
|
||||
const totalOfChatDurationTime = averageOfChatDurationTime.departments.length;
|
||||
|
||||
const sumOfPercentageOfAbandonedRooms = averageOfAbandonedRooms.departments.reduce(
|
||||
(
|
||||
acc: number,
|
||||
abandonedRoom: {
|
||||
percentageOfAbandonedChats: number;
|
||||
},
|
||||
) => {
|
||||
acc += abandonedRoom.percentageOfAbandonedChats;
|
||||
return acc;
|
||||
},
|
||||
0,
|
||||
);
|
||||
const sumOfChatDurationTime = averageOfChatDurationTime.departments.reduce(
|
||||
(
|
||||
acc: number,
|
||||
chatDurationTime: {
|
||||
averageChatDurationTimeInSeconds: number;
|
||||
},
|
||||
) => {
|
||||
acc += chatDurationTime.averageChatDurationTimeInSeconds;
|
||||
return acc;
|
||||
},
|
||||
0,
|
||||
);
|
||||
const totalAbandonedRooms = abandonedRooms.departments.reduce(
|
||||
(
|
||||
acc: number,
|
||||
item: {
|
||||
abandonedRooms: number;
|
||||
},
|
||||
) => {
|
||||
acc += item.abandonedRooms;
|
||||
return acc;
|
||||
},
|
||||
0,
|
||||
);
|
||||
|
||||
const totalOfAverageAbandonedRooms = totalOfAbandonedRooms === 0 ? 0 : sumOfPercentageOfAbandonedRooms / totalOfAbandonedRooms;
|
||||
const totalOfAverageChatDurationTime = totalOfChatDurationTime === 0 ? 0 : sumOfChatDurationTime / totalOfChatDurationTime;
|
||||
|
||||
return {
|
||||
totalizers: [
|
||||
{ title: 'Total_abandoned_chats', value: totalAbandonedRooms },
|
||||
{ title: 'Avg_of_abandoned_chats', value: `${totalOfAverageAbandonedRooms}%` },
|
||||
{
|
||||
title: 'Avg_of_chat_duration_time',
|
||||
value: secondsToHHMMSS(totalOfAverageChatDurationTime),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const getConversationsMetricsAsync = async ({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
user,
|
||||
}: {
|
||||
start: string;
|
||||
end: string;
|
||||
departmentId?: string;
|
||||
user: Pick<IUser, '_id' | 'utcOffset' | 'language'>;
|
||||
}) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const totalizers =
|
||||
(await getAnalyticsOverviewDataCachedForRealtime({
|
||||
daterange: {
|
||||
from: start,
|
||||
to: end,
|
||||
},
|
||||
analyticsOptions: {
|
||||
name: 'Conversations',
|
||||
},
|
||||
...(departmentId && departmentId !== 'undefined' && { departmentId }),
|
||||
utcOffset: user.utcOffset,
|
||||
language: user.language || settings.get('Language') || 'en',
|
||||
executedBy: user._id,
|
||||
})) || [];
|
||||
const metrics = ['Total_conversations', 'Open_conversations', 'On_Hold_conversations', 'Total_messages'];
|
||||
const visitorsCount = await LivechatVisitors.countVisitorsBetweenDate({
|
||||
start: new Date(start),
|
||||
end: new Date(end),
|
||||
department: departmentId,
|
||||
});
|
||||
return {
|
||||
totalizers: [
|
||||
...totalizers.filter((metric: { title: string }) => metrics.includes(metric.title)),
|
||||
{ title: 'Total_visitors', value: visitorsCount },
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const findAllChatMetricsByAgentAsync = async ({
|
||||
start,
|
||||
end,
|
||||
departmentId = undefined,
|
||||
}: {
|
||||
start: Date;
|
||||
end: Date;
|
||||
departmentId?: string;
|
||||
}) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const open = await LivechatRooms.countAllOpenChatsByAgentBetweenDate({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
});
|
||||
const closed = await LivechatRooms.countAllClosedChatsByAgentBetweenDate({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
});
|
||||
const onhold = await LivechatRooms.countAllOnHoldChatsByAgentBetweenDate({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
});
|
||||
|
||||
const result: Record<string, { open: number; closed: number; onhold?: number }> = {};
|
||||
(open || []).forEach((agent: { chats: number; _id: string }) => {
|
||||
result[agent._id] = { open: agent.chats, closed: 0, onhold: 0 };
|
||||
});
|
||||
(closed || []).forEach((agent: { _id: string; chats: number }) => {
|
||||
result[agent._id] = {
|
||||
open: result[agent._id] ? result[agent._id].open : 0,
|
||||
closed: agent.chats,
|
||||
};
|
||||
});
|
||||
|
||||
(onhold || []).forEach((agent: { _id: string; chats: number }) => {
|
||||
result[agent._id] = {
|
||||
...result[agent._id],
|
||||
onhold: agent.chats,
|
||||
};
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
const findAllAgentsStatusAsync = async ({ departmentId = undefined }: { departmentId?: string }) =>
|
||||
(await Users.countAllAgentsStatus({ departmentId }))[0];
|
||||
|
||||
const findAllChatMetricsByDepartmentAsync = async ({
|
||||
start,
|
||||
end,
|
||||
departmentId = undefined,
|
||||
}: {
|
||||
start: Date;
|
||||
end: Date;
|
||||
departmentId?: string;
|
||||
}) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const open = await LivechatRooms.countAllOpenChatsByDepartmentBetweenDate({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
});
|
||||
const closed = await LivechatRooms.countAllClosedChatsByDepartmentBetweenDate({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
});
|
||||
const result: Record<string, { open: number; closed: number }> = {};
|
||||
(open || []).forEach((department: { name: string; chats: number }) => {
|
||||
result[department.name] = { open: department.chats, closed: 0 };
|
||||
});
|
||||
(closed || []).forEach((department: { name: string; chats: number }) => {
|
||||
result[department.name] = {
|
||||
open: result[department.name] ? result[department.name].open : 0,
|
||||
closed: department.chats,
|
||||
};
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
const findAllResponseTimeMetricsAsync = async ({
|
||||
start,
|
||||
end,
|
||||
departmentId = undefined,
|
||||
}: {
|
||||
start: Date;
|
||||
end: Date;
|
||||
departmentId?: string;
|
||||
}) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const responseTimes = (await LivechatRooms.calculateResponseTimingsBetweenDates({ start, end, departmentId }))[0];
|
||||
const reactionTimes = (await LivechatRooms.calculateReactionTimingsBetweenDates({ start, end, departmentId }))[0];
|
||||
const durationTimings = (await LivechatRooms.calculateDurationTimingsBetweenDates({ start, end, departmentId }))[0];
|
||||
|
||||
return {
|
||||
response: {
|
||||
avg: responseTimes ? responseTimes.avg : 0,
|
||||
longest: responseTimes ? responseTimes.longest : 0,
|
||||
},
|
||||
reaction: {
|
||||
avg: reactionTimes ? reactionTimes.avg : 0,
|
||||
longest: reactionTimes ? reactionTimes.longest : 0,
|
||||
},
|
||||
chatDuration: {
|
||||
avg: durationTimings ? durationTimings.avg : 0,
|
||||
longest: durationTimings ? durationTimings.longest : 0,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getConversationsMetricsAsyncCached = mem(getConversationsMetricsAsync, { maxAge: 5000, cacheKey: JSON.stringify });
|
||||
export const getAgentsProductivityMetricsAsyncCached = mem(getAgentsProductivityMetricsAsync, { maxAge: 5000, cacheKey: JSON.stringify });
|
||||
export const getChatsMetricsAsyncCached = mem(getChatsMetricsAsync, { maxAge: 5000, cacheKey: JSON.stringify });
|
||||
export const getProductivityMetricsAsyncCached = mem(getProductivityMetricsAsync, { maxAge: 5000, cacheKey: JSON.stringify });
|
||||
export const findAllChatsStatusAsyncCached = mem(findAllChatsStatusAsync, { maxAge: 5000, cacheKey: JSON.stringify });
|
||||
export const findAllChatMetricsByAgentAsyncCached = mem(findAllChatMetricsByAgentAsync, { maxAge: 5000, cacheKey: JSON.stringify });
|
||||
export const findAllAgentsStatusAsyncCached = mem(findAllAgentsStatusAsync, { maxAge: 5000, cacheKey: JSON.stringify });
|
||||
export const findAllChatMetricsByDepartmentAsyncCached = mem(findAllChatMetricsByDepartmentAsync, {
|
||||
maxAge: 5000,
|
||||
cacheKey: JSON.stringify,
|
||||
});
|
||||
export const findAllResponseTimeMetricsAsyncCached = mem(findAllResponseTimeMetricsAsync, { maxAge: 5000, cacheKey: JSON.stringify });
|
||||
192
apps/meteor/server/lib/omnichannel/analytics/departments.ts
Normal file
192
apps/meteor/server/lib/omnichannel/analytics/departments.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
import { LivechatRooms, Messages } from '@rocket.chat/models';
|
||||
|
||||
import { settings } from '../../../settings';
|
||||
|
||||
type Params = {
|
||||
start: Date;
|
||||
end: Date;
|
||||
options?: any;
|
||||
departmentId?: string;
|
||||
};
|
||||
|
||||
export const findAllRoomsAsync = async ({ start, end, answered, departmentId, options = {} }: Params & { answered?: boolean }) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const total = await LivechatRooms.findAllRooms({
|
||||
start,
|
||||
answered,
|
||||
end,
|
||||
departmentId,
|
||||
onlyCount: true,
|
||||
}).toArray();
|
||||
return {
|
||||
departments: await LivechatRooms.findAllRooms({
|
||||
start,
|
||||
answered,
|
||||
end,
|
||||
departmentId,
|
||||
options,
|
||||
}).toArray(),
|
||||
total: total.length ? total[0].total : 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const findAllAverageOfChatDurationTimeAsync = async ({ start, end, departmentId, options = {} }: Params) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const total = await LivechatRooms.findAllAverageOfChatDurationTime({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
onlyCount: true,
|
||||
}).toArray();
|
||||
return {
|
||||
departments: await LivechatRooms.findAllAverageOfChatDurationTime({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
options,
|
||||
}).toArray(),
|
||||
total: total.length ? total[0].total : 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const findAllAverageServiceTimeAsync = async ({ start, end, departmentId, options = {} }: Params) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const total = await LivechatRooms.findAllAverageOfServiceTime({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
onlyCount: true,
|
||||
}).toArray();
|
||||
return {
|
||||
departments: await LivechatRooms.findAllAverageOfServiceTime({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
options,
|
||||
}).toArray(),
|
||||
total: total.length ? total[0].total : 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const findAllServiceTimeAsync = async ({ start, end, departmentId, options = {} }: Params) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const total = await LivechatRooms.findAllServiceTime({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
onlyCount: true,
|
||||
}).toArray();
|
||||
return {
|
||||
departments: await LivechatRooms.findAllServiceTime({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
options,
|
||||
}).toArray(),
|
||||
total: total.length ? total[0].total : 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const findAllAverageWaitingTimeAsync = async ({ start, end, departmentId, options = {} }: Params) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const total = await LivechatRooms.findAllAverageWaitingTime({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
onlyCount: true,
|
||||
}).toArray();
|
||||
return {
|
||||
departments: await LivechatRooms.findAllAverageWaitingTime({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
options,
|
||||
}).toArray(),
|
||||
total: total.length ? total[0].total : 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const findAllNumberOfTransferredRoomsAsync = async ({ start, end, departmentId, options = {} }: Params) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const total = await Messages.findAllNumberOfTransferredRooms({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
onlyCount: true,
|
||||
}).toArray();
|
||||
return {
|
||||
departments: await Messages.findAllNumberOfTransferredRooms({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
options,
|
||||
}).toArray(),
|
||||
total: total.length ? total[0].total : 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const findAllNumberOfAbandonedRoomsAsync = async ({ start, end, departmentId, options = {} }: Params) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const total = await (
|
||||
await LivechatRooms.findAllNumberOfAbandonedRooms({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
inactivityTimeout: settings.get('Livechat_visitor_inactivity_timeout'),
|
||||
onlyCount: true,
|
||||
})
|
||||
).toArray();
|
||||
return {
|
||||
departments: await (
|
||||
await LivechatRooms.findAllNumberOfAbandonedRooms({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
inactivityTimeout: settings.get('Livechat_visitor_inactivity_timeout'),
|
||||
options,
|
||||
})
|
||||
).toArray(),
|
||||
total: total.length ? total[0].total : 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const findPercentageOfAbandonedRoomsAsync = async ({ start, end, departmentId, options = {} }: Params) => {
|
||||
if (!start || !end) {
|
||||
throw new Error('"start" and "end" must be provided');
|
||||
}
|
||||
const total = await (
|
||||
await LivechatRooms.findPercentageOfAbandonedRooms({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
inactivityTimeout: settings.get('Livechat_visitor_inactivity_timeout'),
|
||||
onlyCount: true,
|
||||
})
|
||||
).toArray();
|
||||
return {
|
||||
departments: await (
|
||||
await LivechatRooms.findPercentageOfAbandonedRooms({
|
||||
start,
|
||||
end,
|
||||
departmentId,
|
||||
inactivityTimeout: settings.get('Livechat_visitor_inactivity_timeout'),
|
||||
options,
|
||||
})
|
||||
).toArray(),
|
||||
total: total.length ? total[0].total : 0,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user