fix: Add attendees to getLuckyUser algorithm (#10285)
* Add attendees to getLuckyUser algorithm * Fix unit test for getLuckyUserpull/10289/head
parent
56f4310c24
commit
3818093652
|
@ -33,6 +33,7 @@ it("can find lucky user with maximize availability", async () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
prismaMock.user.findMany.mockResolvedValue(users);
|
prismaMock.user.findMany.mockResolvedValue(users);
|
||||||
|
prismaMock.booking.findMany.mockResolvedValue([]);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
getLuckyUser("MAXIMIZE_AVAILABILITY", {
|
getLuckyUser("MAXIMIZE_AVAILABILITY", {
|
||||||
|
|
|
@ -2,14 +2,15 @@ import type { User } from "@prisma/client";
|
||||||
|
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
async function leastRecentlyBookedUser<T extends Pick<User, "id">>({
|
async function leastRecentlyBookedUser<T extends Pick<User, "id" | "email">>({
|
||||||
availableUsers,
|
availableUsers,
|
||||||
eventTypeId,
|
eventTypeId,
|
||||||
}: {
|
}: {
|
||||||
availableUsers: T[];
|
availableUsers: T[];
|
||||||
eventTypeId: number;
|
eventTypeId: number;
|
||||||
}) {
|
}) {
|
||||||
const usersWithLastCreated = await prisma.user.findMany({
|
// First we get all organizers (fixed host/single round robin user)
|
||||||
|
const organizersWithLastCreated = await prisma.user.findMany({
|
||||||
where: {
|
where: {
|
||||||
id: {
|
id: {
|
||||||
in: availableUsers.map((user) => user.id),
|
in: availableUsers.map((user) => user.id),
|
||||||
|
@ -32,18 +33,64 @@ async function leastRecentlyBookedUser<T extends Pick<User, "id">>({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!usersWithLastCreated) {
|
const organizerIdAndAtCreatedPair = organizersWithLastCreated.reduce(
|
||||||
throw new Error("Unable to find users by availableUser ids."); // should never happen.
|
(keyValuePair: { [userId: number]: Date }, user) => {
|
||||||
}
|
|
||||||
|
|
||||||
const userIdAndAtCreatedPair = usersWithLastCreated.reduce(
|
|
||||||
(keyValuePair: { [key: number]: Date }, user) => {
|
|
||||||
keyValuePair[user.id] = user.bookings[0]?.createdAt || new Date(0);
|
keyValuePair[user.id] = user.bookings[0]?.createdAt || new Date(0);
|
||||||
return keyValuePair;
|
return keyValuePair;
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const bookings = await prisma.booking.findMany({
|
||||||
|
where: {
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
eventTypeId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attendees: {
|
||||||
|
some: {
|
||||||
|
email: {
|
||||||
|
in: availableUsers.map((user) => user.email),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
createdAt: true,
|
||||||
|
attendees: {
|
||||||
|
select: {
|
||||||
|
email: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: "desc",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const attendeeUserIdAndAtCreatedPair = bookings.reduce((aggregate: { [userId: number]: Date }, booking) => {
|
||||||
|
availableUsers.forEach((user) => {
|
||||||
|
if (aggregate[user.id]) return; // Bookings are ordered DESC, so if the reducer aggregate
|
||||||
|
// contains the user id, it's already got the most recent booking marked.
|
||||||
|
if (!booking.attendees.map((attendee) => attendee.email).includes(user.email)) return;
|
||||||
|
aggregate[user.id] = booking.createdAt;
|
||||||
|
});
|
||||||
|
return aggregate;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const userIdAndAtCreatedPair = {
|
||||||
|
...organizerIdAndAtCreatedPair,
|
||||||
|
...attendeeUserIdAndAtCreatedPair,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!userIdAndAtCreatedPair) {
|
||||||
|
throw new Error("Unable to find users by availableUser ids."); // should never happen.
|
||||||
|
}
|
||||||
|
|
||||||
const leastRecentlyBookedUser = availableUsers.sort((a, b) => {
|
const leastRecentlyBookedUser = availableUsers.sort((a, b) => {
|
||||||
return userIdAndAtCreatedPair[a.id] > userIdAndAtCreatedPair[b.id] ? 1 : -1;
|
return userIdAndAtCreatedPair[a.id] > userIdAndAtCreatedPair[b.id] ? 1 : -1;
|
||||||
})[0];
|
})[0];
|
||||||
|
@ -53,7 +100,7 @@ async function leastRecentlyBookedUser<T extends Pick<User, "id">>({
|
||||||
|
|
||||||
// TODO: Configure distributionAlgorithm from the event type configuration
|
// TODO: Configure distributionAlgorithm from the event type configuration
|
||||||
// TODO: Add 'MAXIMIZE_FAIRNESS' algorithm.
|
// TODO: Add 'MAXIMIZE_FAIRNESS' algorithm.
|
||||||
export async function getLuckyUser<T extends Pick<User, "id">>(
|
export async function getLuckyUser<T extends Pick<User, "id" | "email">>(
|
||||||
distributionAlgorithm: "MAXIMIZE_AVAILABILITY" = "MAXIMIZE_AVAILABILITY",
|
distributionAlgorithm: "MAXIMIZE_AVAILABILITY" = "MAXIMIZE_AVAILABILITY",
|
||||||
{ availableUsers, eventTypeId }: { availableUsers: T[]; eventTypeId: number }
|
{ availableUsers, eventTypeId }: { availableUsers: T[]; eventTypeId: number }
|
||||||
) {
|
) {
|
||||||
|
|
Loading…
Reference in New Issue