All files / server/src/features/assignment/utils getEvents.ts

100% Statements 9/9
100% Branches 2/2
100% Functions 3/3
100% Lines 9/9

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          51x           128x 652x           128x   207x             207x       207x         207x                           128x    
import { shuffle } from "remeda";
import { ProgramItem } from "shared/types/models/programItem";
import { DirectSignupsForProgramItem } from "server/features/direct-signup/directSignupTypes";
import { Event } from "server/types/assignmentTypes";
 
export const getEvents = (
  lotterySignupProgramItems: readonly ProgramItem[],
  lotteryParticipantDirectSignups: readonly DirectSignupsForProgramItem[],
): Event[] => {
  // Indexed rather than scanned: the sign-up documents cover the whole event, and this asks for
  // one per program item being lotteried, so scanning would walk the event once for each
  const signupsByProgramItemId = new Map(
    lotteryParticipantDirectSignups.map((signup) => [
      signup.programItemId,
      signup,
    ]),
  );
 
  const programItems = lotterySignupProgramItems.map(
    (lotterySignupProgramItem) => {
      const programItemSignup = signupsByProgramItemId.get(
        lotterySignupProgramItem.programItemId,
      );
 
      // A spot already held is not the lottery's to hand out, whichever hour it was taken for.
      // A program item being lotteried holds none, so this is defence in depth: offering the
      // seats anyway would have the assigner over-fill it and the save drop groups back out
      const currentSignups = programItemSignup?.userSignups.length ?? 0;
 
      // Capacity can't go negative: a program item whose attendance limit was lowered
      // below the number of attendees already in it offers no spots rather than negative ones
      const remainingMax = Math.max(
        lotterySignupProgramItem.maxAttendance - currentSignups,
        0,
      );
 
      return {
        id: lotterySignupProgramItem.programItemId,
        // Floored at one attendee while spots remain, and capped by them, so the assigner
        // never receives min > max
        min: Math.min(
          Math.max(lotterySignupProgramItem.minAttendance - currentSignups, 1),
          remainingMax,
        ),
        max: remainingMax,
        groups: [],
      };
    },
  );
 
  return shuffle(programItems);
};