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
|
||||
// @ts-ignore
|
||||
prismaMock.user.findMany.mockResolvedValue(users);
|
||||
prismaMock.booking.findMany.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
getLuckyUser("MAXIMIZE_AVAILABILITY", {
|
||||
|
|
|
@ -2,14 +2,15 @@ import type { User } from "@prisma/client";
|
|||
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
async function leastRecentlyBookedUser<T extends Pick<User, "id">>({
|
||||
async function leastRecentlyBookedUser<T extends Pick<User, "id" | "email">>({
|
||||
availableUsers,
|
||||
eventTypeId,
|
||||
}: {
|
||||
availableUsers: T[];
|
||||
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: {
|
||||
id: {
|
||||
in: availableUsers.map((user) => user.id),
|
||||
|
@ -32,18 +33,64 @@ async function leastRecentlyBookedUser<T extends Pick<User, "id">>({
|
|||
},
|
||||
});
|
||||
|
||||
if (!usersWithLastCreated) {
|
||||
throw new Error("Unable to find users by availableUser ids."); // should never happen.
|
||||
}
|
||||
|
||||
const userIdAndAtCreatedPair = usersWithLastCreated.reduce(
|
||||
(keyValuePair: { [key: number]: Date }, user) => {
|
||||
const organizerIdAndAtCreatedPair = organizersWithLastCreated.reduce(
|
||||
(keyValuePair: { [userId: number]: Date }, user) => {
|
||||
keyValuePair[user.id] = user.bookings[0]?.createdAt || new Date(0);
|
||||
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) => {
|
||||
return userIdAndAtCreatedPair[a.id] > userIdAndAtCreatedPair[b.id] ? 1 : -1;
|
||||
})[0];
|
||||
|
@ -53,7 +100,7 @@ async function leastRecentlyBookedUser<T extends Pick<User, "id">>({
|
|||
|
||||
// TODO: Configure distributionAlgorithm from the event type configuration
|
||||
// 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",
|
||||
{ availableUsers, eventTypeId }: { availableUsers: T[]; eventTypeId: number }
|
||||
) {
|
||||
|
|
Loading…
Reference in New Issue