87 lines
3.4 KiB
TypeScript
87 lines
3.4 KiB
TypeScript
|
|
/**
|
||
|
|
* Pure helpers for the Simulator Setup page, split out so they can be unit tested
|
||
|
|
* without pulling the route's server-only imports into the test.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import type {
|
||
|
|
BaseEloKey,
|
||
|
|
ResolvedRating,
|
||
|
|
ResolvedSourceElo,
|
||
|
|
} from "~/services/simulations/input-policy";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Short badge text for how a participant's Elo or rating was produced, or null for a
|
||
|
|
* directly entered one — the unremarkable case, which needs no badge.
|
||
|
|
*
|
||
|
|
* The preview table needs this because a generated value is deliberately hidden from
|
||
|
|
* `getParticipantSimulatorInputs`, so without the resolved value plus this label the
|
||
|
|
* row reads as "nothing saved" and a projection losing to a raw Elo is invisible.
|
||
|
|
*
|
||
|
|
* Every remaining method is a missing-input fallback (`fallbackElo`,
|
||
|
|
* `fallbackRating`, `averageKnown`, `worstKnownMinus`, `block`), which all read the
|
||
|
|
* same way to an admin: this participant had nothing usable of its own.
|
||
|
|
*/
|
||
|
|
export function resolvedInputMethodLabel(
|
||
|
|
method: ResolvedSourceElo["method"] | ResolvedRating["method"]
|
||
|
|
): string | null {
|
||
|
|
switch (method) {
|
||
|
|
case "direct":
|
||
|
|
return null;
|
||
|
|
case "projectedWins":
|
||
|
|
case "projectedTablePoints":
|
||
|
|
return "from projections";
|
||
|
|
case "sourceOdds":
|
||
|
|
return "from futures";
|
||
|
|
case "blend":
|
||
|
|
return "blended";
|
||
|
|
default:
|
||
|
|
return "fallback";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Method flag for a bulk-input row that carries a projection instead of an Elo, or
|
||
|
|
* undefined when the row says nothing about how its Elo was produced.
|
||
|
|
*
|
||
|
|
* A row supplying a projection but no explicit Elo means "derive the Elo from this
|
||
|
|
* projection". Stamping the flag marks whatever Elo is already stored as generated,
|
||
|
|
* so `getParticipantSimulatorInputs` hides it and `resolveSourceElos` re-derives
|
||
|
|
* from the projection — without it, the non-destructive upsert leaves a stale
|
||
|
|
* hand-entered Elo in place, and that Elo wins the `baseEloPriority` race so the
|
||
|
|
* projection is written to the database and then ignored on every run.
|
||
|
|
*
|
||
|
|
* Returning undefined (rather than an empty object) matters: the upsert only
|
||
|
|
* preserves existing metadata, and clears a stale flag for a fresh direct Elo, when
|
||
|
|
* the incoming metadata is null.
|
||
|
|
*/
|
||
|
|
export function projectionMethodMetadata(
|
||
|
|
sourceElo: number | undefined,
|
||
|
|
projectedWins: number | undefined,
|
||
|
|
projectedTablePoints: number | undefined
|
||
|
|
): Record<string, unknown> | undefined {
|
||
|
|
if (sourceElo !== undefined) return undefined;
|
||
|
|
if (projectedWins !== undefined) return { sourceEloMethod: "projectedWins" };
|
||
|
|
if (projectedTablePoints !== undefined) return { sourceEloMethod: "projectedTablePoints" };
|
||
|
|
return undefined;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Translate the Base Elo Source select into a full `baseEloPriority` list. Only the
|
||
|
|
* head of the list is user-facing (raw Elo vs. projections); the remaining keys keep
|
||
|
|
* their existing relative order so a season that already has a custom ordering is
|
||
|
|
* not silently flattened.
|
||
|
|
*/
|
||
|
|
export function parseBaseEloPriorityChoice(
|
||
|
|
value: FormDataEntryValue | null,
|
||
|
|
current: BaseEloKey[]
|
||
|
|
): BaseEloKey[] {
|
||
|
|
// The select only renders for simulators that can derive Elo from a projection.
|
||
|
|
// When it was not on the form there is no choice to apply, so keep what is stored
|
||
|
|
// rather than silently rewriting the season's ordering.
|
||
|
|
if (value === null) return current;
|
||
|
|
const projections = current.filter((key) => key !== "sourceElo");
|
||
|
|
return value === "projectionsFirst"
|
||
|
|
? [...projections, "sourceElo"]
|
||
|
|
: ["sourceElo", ...projections];
|
||
|
|
}
|