fix(stats): average response/resolution times

This commit is contained in:
Isaac 2023-05-06 00:06:39 +01:00
parent d7e538ee3c
commit cf93f085ac
No known key found for this signature in database
GPG Key ID: 0DE40AE37BBA5C33
6 changed files with 36 additions and 14 deletions

2
src/lib/stats.js Normal file
View File

@ -0,0 +1,2 @@
module.exports.getAvgResolutionTime = tickets => (tickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) ?? 1) / tickets.length;
module.exports.getAvgResponseTime = tickets => (tickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) ?? 1) / tickets.length;

View File

@ -20,6 +20,10 @@ const { isStaff } = require('../users');
const { Collection } = require('discord.js'); const { Collection } = require('discord.js');
const spacetime = require('spacetime'); const spacetime = require('spacetime');
const Cryptr = require('cryptr'); const Cryptr = require('cryptr');
const {
getAvgResolutionTime,
getAvgResponseTime,
} = require('../stats');
const { const {
decrypt, decrypt,
encrypt, encrypt,
@ -404,10 +408,11 @@ module.exports = class TicketManager {
if (category.image) await channel.send(category.image); if (category.image) await channel.send(category.image);
const needsStats = category.openingMessage.match(/{+\s?avgResponseTime\s?}+/i) || category.openingMessage.match(/{+\s?avgResolutionTime\s?}+/i);
const statsCacheKey = `cache/category-stats/${categoryId}`; const statsCacheKey = `cache/category-stats/${categoryId}`;
let stats = await this.client.keyv.get(statsCacheKey); let stats = await this.client.keyv.get(statsCacheKey);
if (!stats) { if (needsStats && !stats) {
const tickets = await this.client.prisma.ticket.findMany({ const closedTickets = await this.client.prisma.ticket.findMany({
select: { select: {
closedAt: true, closedAt: true,
createdAt: true, createdAt: true,
@ -420,8 +425,8 @@ module.exports = class TicketManager {
}, },
}); });
stats = { stats = {
avgResolutionTime: ms(tickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) ?? 1 / tickets.length, { long: true }), avgResolutionTime: ms(getAvgResolutionTime(closedTickets), { long: true }),
avgResponseTime: ms(tickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) ?? 1 / tickets.length, { long: true }), avgResponseTime: ms(getAvgResponseTime(closedTickets), { long: true }),
}; };
this.client.keyv.set(statsCacheKey, stats, ms('1h')); this.client.keyv.set(statsCacheKey, stats, ms('1h'));
} }

View File

@ -1,5 +1,9 @@
const { Listener } = require('@eartharoid/dbf'); const { Listener } = require('@eartharoid/dbf');
const crypto = require('crypto'); const crypto = require('crypto');
const {
getAvgResolutionTime,
getAvgResponseTime,
} = require('../../lib/stats');
const ms = require('ms'); const ms = require('ms');
const { version } = require('../../../package.json'); const { version } = require('../../../package.json');
const { msToMins } = require('../../lib/misc'); const { msToMins } = require('../../lib/misc');
@ -58,8 +62,8 @@ module.exports = class extends Listener {
}); });
const closedTickets = tickets.filter(t => t.firstResponseAt && t.closedAt); const closedTickets = tickets.filter(t => t.firstResponseAt && t.closedAt);
cached = { cached = {
avgResolutionTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), avgResolutionTime: ms(getAvgResolutionTime(closedTickets)),
avgResponseTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), avgResponseTime: ms(getAvgResponseTime(closedTickets)),
openTickets: tickets.length - closedTickets.length, openTickets: tickets.length - closedTickets.length,
totalTickets: tickets.length, totalTickets: tickets.length,
}; };

View File

@ -6,6 +6,10 @@ const {
ChannelType: { GuildCategory }, ChannelType: { GuildCategory },
} = require('discord.js'); } = require('discord.js');
const ms = require('ms'); const ms = require('ms');
const {
getAvgResolutionTime,
getAvgResponseTime,
} = require('../../../../../../lib/stats');
module.exports.get = fastify => ({ module.exports.get = fastify => ({
handler: async (req, res) => { handler: async (req, res) => {
@ -36,8 +40,8 @@ module.exports.get = fastify => ({
c = { c = {
...c, ...c,
stats: { stats: {
avgResolutionTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), avgResolutionTime: ms(getAvgResolutionTime(closedTickets)),
avgResponseTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), avgResponseTime: ms(getAvgResponseTime(closedTickets)),
}, },
}; };
delete c.tickets; delete c.tickets;

View File

@ -1,4 +1,8 @@
/* eslint-disable no-underscore-dangle */ /* eslint-disable no-underscore-dangle */
const {
getAvgResolutionTime,
getAvgResponseTime,
} = require('../../../../../lib/stats');
const ms = require('ms'); const ms = require('ms');
module.exports.get = fastify => ({ module.exports.get = fastify => ({
@ -36,8 +40,8 @@ module.exports.get = fastify => ({
logo: guild.iconURL(), logo: guild.iconURL(),
name: guild.name, name: guild.name,
stats: { stats: {
avgResolutionTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), avgResolutionTime: ms(getAvgResolutionTime(closedTickets)),
avgResponseTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), avgResponseTime: ms(getAvgResponseTime(closedTickets)),
categories: categories.map(c => ({ categories: categories.map(c => ({
id: c.id, id: c.id,
name: c.name, name: c.name,

View File

@ -1,3 +1,6 @@
const {
getAvgResolutionTime, getAvgResponseTime,
} = require('../../lib/stats');
const ms = require('ms'); const ms = require('ms');
module.exports.get = () => ({ module.exports.get = () => ({
@ -25,8 +28,8 @@ module.exports.get = () => ({
stats: { stats: {
activatedUsers: users.length, activatedUsers: users.length,
archivedMessages: users.reduce((total, user) => total + user.messageCount, 0), // don't count archivedMessage table rows, they can be deleted archivedMessages: users.reduce((total, user) => total + user.messageCount, 0), // don't count archivedMessage table rows, they can be deleted
avgResolutionTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.closedAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), avgResolutionTime: ms(getAvgResolutionTime(closedTickets)),
avgResponseTime: ms(closedTickets.reduce((total, ticket) => total + (ticket.firstResponseAt - ticket.createdAt), 0) ?? 1 / closedTickets.length), avgResponseTime: ms(getAvgResponseTime(closedTickets)),
categories: await client.prisma.category.count(), categories: await client.prisma.category.count(),
guilds: client.guilds.cache.size, guilds: client.guilds.cache.size,
members: client.guilds.cache.reduce((t, g) => t + g.memberCount, 0), members: client.guilds.cache.reduce((t, g) => t + g.memberCount, 0),