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

100% Statements 23/23
100% Branches 17/17
100% Functions 9/9
100% Lines 23/23

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                  47x                     882x 986x       882x 1291x   161423x   19270x                   1291x     531x 531x   4418x   531x               1291x 23x   1268x         882x   882x               882x 1268x   522x             882x   882x       882x    
import dayjs from "dayjs";
import { partition } from "remeda";
import { config } from "shared/config";
import { DirectSignupsForProgramItem } from "server/features/direct-signup/directSignupTypes";
import { User } from "shared/types/models/user";
import { EventLogAction } from "shared/types/models/eventLog";
import { ProgramItem } from "shared/types/models/programItem";
import { DIRECT_SIGNUP_PRIORITY } from "shared/constants/signups";
 
export const getAssignmentBonus = (
  attendeeGroup: User[],
  lotteryParticipantDirectSignups: readonly DirectSignupsForProgramItem[],
  lotterySignupProgramItems: readonly ProgramItem[],
  assignmentTime: string,
): number => {
  /** First time bonus */
 
  // A re-run must not count its own results as "previous" (which would strip the bonus and
  // change outcomes): ignore lottery wins (priority > 0) and NEW_ASSIGNMENT events at the
  // current assignmentTime, but keep genuine first-come-first-served direct signups
  const isCurrentAssignment = (startTime: string): boolean =>
    dayjs(startTime).isSame(dayjs(assignmentTime), "minute");
 
  // Get group members with previous direct signups or NEW_ASSIGNMENT event log items
  const [groupMembersWithDirectSignups, groupMembersWithoutDirectSignups] =
    partition(attendeeGroup, (groupMember) => {
      const previousDirectSignup = lotteryParticipantDirectSignups.find(
        (programItem) => {
          return programItem.userSignups.find(
            (userSignup) =>
              userSignup.username === groupMember.username &&
              // Exclude this lottery's own win (priority > 0) at the current time, but keep
              // first-come-first-served (priority 0) signups counting as "previous"
              !(
                isCurrentAssignment(userSignup.signedToStartTime) &&
                userSignup.priority !== DIRECT_SIGNUP_PRIORITY
              ),
          );
        },
      );
      const newAssignmentEvent = groupMember.eventLogItems.find(
        (eventLogItem) => {
          const previousAssignment =
            eventLogItem.action === EventLogAction.NEW_ASSIGNMENT;
          const programItemExists = lotterySignupProgramItems.some(
            (programItem) =>
              programItem.programItemId === eventLogItem.programItemId,
          );
          return (
            previousAssignment &&
            programItemExists &&
            !isCurrentAssignment(eventLogItem.programItemStartTime)
          );
        },
      );
 
      if (previousDirectSignup ?? newAssignmentEvent) {
        return true;
      }
      return false;
    });
 
  // Give first time bonus to the whole group if half of the group members don't have previous direct signups
  const averagePreviousDirectSignups =
    groupMembersWithDirectSignups.length / attendeeGroup.length;
  const firstTimeBonus =
    averagePreviousDirectSignups <= 0.5 ? config.server().firstSignupBonus : 0;
 
  /** Additional first time bonus */
 
  // Get group members with previous NO_ASSIGNMENT event log items and without direct signups.
  // Ignore a NO_ASSIGNMENT from the current assignmentTime — on a re-run it's this run's own
  // earlier result, so counting it would make the re-run boost run-1 failures
  const groupMembersWithPreviousFailedLotterySignup =
    groupMembersWithoutDirectSignups.filter((groupMember) => {
      return groupMember.eventLogItems.find(
        (eventLogItem) =>
          eventLogItem.action === EventLogAction.NO_ASSIGNMENT &&
          !isCurrentAssignment(eventLogItem.programItemStartTime),
      );
    });
 
  // Give additional first time bonus to the whole group if half of the group members have previous failed lottery signups
  const averageFailedLotterySignups =
    groupMembersWithPreviousFailedLotterySignup.length / attendeeGroup.length;
  const additionalFirstTimeBonus =
    averageFailedLotterySignups >= 0.5
      ? config.server().additionalFirstSignupBonus
      : 0;
 
  return firstTimeBonus + additionalFirstTimeBonus;
};