All files / libs/lti/shared/src/lib get-earned-ratio-for-kpi.ts

94.64% Statements 53/56
87.5% Branches 7/8
100% Functions 2/2
94.64% Lines 53/56

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 571x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x       1x 1x 1x 1x 1x 1x 1x 1x 1x  
import { match } from 'ts-pattern';
 
import { type LtiPlanKpi } from '@amalia/lti/types';
 
/**
 * TODO: Works only for LINEAR KPI configuration.
 *
 * @param kpi
 * @param value
 */
export const getEarnedRatioForKpi = (kpi: Pick<LtiPlanKpi, 'configuration'>, value: number) => {
  const steps = kpi.configuration.steps;
 
  // plateau check (below first step or above last step)
  const lastStep = steps.at(-1);
  const plateauValue = match(value)
    .when(
      (v) => v < steps[0][0],
      () => steps[0][1],
    )
    .when(
      (v) => !!lastStep && v >= lastStep[0],
      () => lastStep![1],
    )
    .otherwise(() => undefined);
 
  if (plateauValue !== undefined) {
    return plateauValue;
  }
 
  let underStepIndex = -1;
  let upperStepIndex = -1;
 
  for (let i = 0; i < steps.length - 1; i++) {
    const [underTier] = steps[i];
    const [upperTier] = steps[i + 1];
    if (value >= underTier && value < upperTier) {
      underStepIndex = i;
      upperStepIndex = i + 1;
      break;
    }
  }
 
  if (underStepIndex === -1 || upperStepIndex === -1) {
    // should not happen
    throw new Error('Bad code');
  }
 
  // Calculate linear interpolation
  const [underTier, underEarned] = steps[underStepIndex];
  const [upperTier, upperEarned] = steps[upperStepIndex];
 
  const slope = (upperEarned - underEarned) / (upperTier - underTier);
 
  return underEarned + slope * (value - underTier);
};