Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | 60x 603x 603x 603x 456x 603x 200x 344x 603x 8743x 603x 14x 14x 603x 60x 603x 589x 335x 205x 205x 14x 13x 14x 205x 205x 1184x 5x 1180x 1184x 1183x 1180x 14x 13x 201x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | import { MongoDbError } from "shared/types/api/errors";
import { EventLogAction } from "shared/types/models/eventLog";
import { ProgramItem } from "shared/types/models/programItem";
import { isLotterySignupProgramItem } from "shared/utils/isLotterySignupProgramItem";
import { Result, makeSuccessResult } from "shared/utils/result";
import {
getDirectSignupPhaseStarted,
getLotterySignupEnded,
} from "shared/utils/signupTimes";
import { getTimeNow } from "server/features/assignment/utils/getTimeNow";
import { DirectSignupsForProgramItem } from "server/features/direct-signup/directSignupTypes";
import { queueCancelledDeletedEmails } from "server/features/notifications/queueCancelledDeletedEmails";
import { addEventLogItems } from "server/features/user/event-log/eventLogRepository";
import {
DeleteLotterySignupsParams,
delLotterySignups,
} from "server/features/user/lottery-signup/lotterySignupRepository";
import { findUsers } from "server/features/user/userRepository";
import { logger } from "server/utils/logger";
// A lottery program item holding sign-ups goes to direct sign-up instead. Decided as the
// programme is saved rather than when a run reaches it, so the program item can say so up front,
// and the mark is what keeps the decision once those sign-ups are cancelled.
export const getPassedOverProgramItems = async (
updatedProgramItems: readonly ProgramItem[],
currentProgramItems: readonly ProgramItem[],
directSignups: readonly DirectSignupsForProgramItem[],
): Promise<Result<ProgramItem[], MongoDbError>> => {
const timeNowResult = await getTimeNow();
Iif (!timeNowResult.ok) {
return timeNowResult;
}
const attendeeCountByProgramItemId = new Map(
directSignups.map((directSignup) => [
directSignup.programItemId,
directSignup.userSignups.length,
]),
);
// What is stored is what was there before this save, since neither field is in the import's
// update - a program item the database does not know about is a new one and carries neither
const decidedProgramItemIds = new Set(
currentProgramItems
.filter(
(programItem) =>
programItem.lotteryRanForStartTime !== undefined ||
programItem.passedOverForLottery === true,
)
.map((programItem) => programItem.programItemId),
);
const passedOverProgramItems = updatedProgramItems.filter(
(programItem) =>
isLotterySignupProgramItem(programItem) &&
!decidedProgramItemIds.has(programItem.programItemId) &&
(attendeeCountByProgramItemId.get(programItem.programItemId) ?? 0) > 0 &&
// Only while its schedule still has something to take away. Measured to the moment
// direct sign-up would open rather than to the lottery's close, so a program item that
// becomes a lottery one inside the phase gap does not have its open sign-up shut for
// the rest of it.
!getDirectSignupPhaseStarted(programItem, timeNowResult.value),
);
if (passedOverProgramItems.length > 0) {
logger.info(
`${passedOverProgramItems.length} program items already hold sign-ups, marking them as not taking part in a lottery: ${passedOverProgramItems.map((programItem) => programItem.programItemId).join(", ")}`,
);
}
return makeSuccessResult(passedOverProgramItems);
};
// Lottery sign-ups for a program item that will not be lotteried after all. They are for a
// lottery that will not happen, so they go the same way as those for a program item whose program
// type leaves the lottery, and say the same thing. A sign-up whose lottery has already run is
// left alone, whatever happens to the program item afterwards.
export const removePassedOverLotterySignups = async (
passedOverProgramItems: readonly ProgramItem[],
): Promise<Result<void, MongoDbError>> => {
if (passedOverProgramItems.length === 0) {
return makeSuccessResult();
}
const timeNowResult = await getTimeNow();
Iif (!timeNowResult.ok) {
return timeNowResult;
}
const passedOverProgramItemIds = new Set(
passedOverProgramItems
.filter(
(programItem) =>
!getLotterySignupEnded(programItem, timeNowResult.value),
)
.map((programItem) => programItem.programItemId),
);
const usersResult = await findUsers();
Iif (!usersResult.ok) {
return usersResult;
}
const usersToUpdate: DeleteLotterySignupsParams[] = usersResult.value.flatMap(
(user) => {
const lotterySignupProgramItemIds = user.lotterySignups
.filter((lotterySignup) =>
passedOverProgramItemIds.has(lotterySignup.programItemId),
)
.map((lotterySignup) => lotterySignup.programItemId);
if (lotterySignupProgramItemIds.length === 0) {
return [];
}
return { username: user.username, lotterySignupProgramItemIds };
},
);
if (usersToUpdate.length === 0) {
return makeSuccessResult();
}
const delLotterySignupsResult = await delLotterySignups(usersToUpdate);
Iif (!delLotterySignupsResult.ok) {
return delLotterySignupsResult;
}
const passedOverProgramItemsById = new Map(
passedOverProgramItems.map((programItem) => [
programItem.programItemId,
programItem,
]),
);
const eventUpdates = usersToUpdate.flatMap((user) =>
user.lotterySignupProgramItemIds.flatMap((programItemId) => {
const programItem = passedOverProgramItemsById.get(programItemId);
Iif (!programItem) {
return [];
}
return {
username: user.username,
programItemId: programItem.programItemId,
programItemStartTime: programItem.startTime,
createdAt: new Date().toISOString(),
action: EventLogAction.PROGRAM_ITEM_NO_LOTTERY_ANYMORE,
};
}),
);
const addEventLogItemsResult = await addEventLogItems(eventUpdates);
Iif (!addEventLogItemsResult.ok) {
return addEventLogItemsResult;
}
await queueCancelledDeletedEmails(
eventUpdates,
new Map(
passedOverProgramItems.map((programItem) => [
programItem.programItemId,
programItem.title,
]),
),
);
return makeSuccessResult();
};
|