Compare commits
No commits in common. "350620ca675274394916aef62e78ba4293321787" and "f4bf7ff723706d4097b29889947e07504a87dc60" have entirely different histories.
350620ca67
...
f4bf7ff723
7 changed files with 32 additions and 55 deletions
|
|
@ -23,7 +23,6 @@ describe("QualifyingPointsStandings", () => {
|
||||||
totalQualifyingPoints: "14.67",
|
totalQualifyingPoints: "14.67",
|
||||||
eventsScored: 1,
|
eventsScored: 1,
|
||||||
finalRanking: null,
|
finalRanking: null,
|
||||||
globalRank: 1,
|
|
||||||
participant: { id: "participant-1", name: "Player One" },
|
participant: { id: "participant-1", name: "Player One" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -31,7 +30,6 @@ describe("QualifyingPointsStandings", () => {
|
||||||
totalQualifyingPoints: "0.50",
|
totalQualifyingPoints: "0.50",
|
||||||
eventsScored: 1,
|
eventsScored: 1,
|
||||||
finalRanking: null,
|
finalRanking: null,
|
||||||
globalRank: 2,
|
|
||||||
participant: { id: "participant-2", name: "Player Two" },
|
participant: { id: "participant-2", name: "Player Two" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -39,7 +37,6 @@ describe("QualifyingPointsStandings", () => {
|
||||||
totalQualifyingPoints: "0.43",
|
totalQualifyingPoints: "0.43",
|
||||||
eventsScored: 1,
|
eventsScored: 1,
|
||||||
finalRanking: null,
|
finalRanking: null,
|
||||||
globalRank: 3,
|
|
||||||
participant: { id: "participant-3", name: "Player Three" },
|
participant: { id: "participant-3", name: "Player Three" },
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ describe("SportsSection", () => {
|
||||||
await user.click(screen.getByText("2025 MLB Season"));
|
await user.click(screen.getByText("2025 MLB Season"));
|
||||||
|
|
||||||
expect(mlbCheckbox).toBeChecked();
|
expect(mlbCheckbox).toBeChecked();
|
||||||
}, 15000);
|
});
|
||||||
|
|
||||||
it("submits the currently selected sports seasons after toggling", async () => {
|
it("submits the currently selected sports seasons after toggling", async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ interface QPStanding {
|
||||||
totalQualifyingPoints: string;
|
totalQualifyingPoints: string;
|
||||||
eventsScored: number;
|
eventsScored: number;
|
||||||
finalRanking: number | null;
|
finalRanking: number | null;
|
||||||
globalRank: number;
|
|
||||||
participant: {
|
participant: {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -71,11 +70,26 @@ export function QualifyingPointsStandings({
|
||||||
const ownershipMap = new Map(teamOwnerships.map((o) => [o.participantId, o]));
|
const ownershipMap = new Map(teamOwnerships.map((o) => [o.participantId, o]));
|
||||||
const userParticipantSet = new Set(userParticipantIds);
|
const userParticipantSet = new Set(userParticipantIds);
|
||||||
|
|
||||||
// Use the pre-computed global rank from the loader so displayed ranks reflect the full
|
// Assign current ranks with tie handling
|
||||||
// participant field even when the array has been filtered down to drafted/points-earning rows.
|
const rankedStandings: Array<QPStanding & { currentRank: number }> = [];
|
||||||
const rankedStandings: Array<QPStanding & { currentRank: number }> = standings.map(
|
let previousQP = -1;
|
||||||
(standing) => ({ ...standing, currentRank: standing.globalRank })
|
let previousRankStart = -1;
|
||||||
);
|
|
||||||
|
for (let index = 0; index < standings.length; index++) {
|
||||||
|
const standing = standings[index];
|
||||||
|
const currentQP = parseFloat(standing.totalQualifyingPoints);
|
||||||
|
|
||||||
|
let currentRank: number;
|
||||||
|
if (index > 0 && Math.abs(currentQP - previousQP) < 0.001) {
|
||||||
|
currentRank = previousRankStart;
|
||||||
|
} else {
|
||||||
|
currentRank = index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rankedStandings.push({ ...standing, currentRank });
|
||||||
|
previousRankStart = currentRank;
|
||||||
|
previousQP = currentQP;
|
||||||
|
}
|
||||||
|
|
||||||
const tiedCountByRank = new Map<number, number>();
|
const tiedCountByRank = new Map<number, number>();
|
||||||
for (const s of rankedStandings) {
|
for (const s of rankedStandings) {
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,6 @@ interface QPStanding {
|
||||||
totalQualifyingPoints: string;
|
totalQualifyingPoints: string;
|
||||||
eventsScored: number;
|
eventsScored: number;
|
||||||
finalRanking: number | null;
|
finalRanking: number | null;
|
||||||
globalRank: number;
|
|
||||||
participant: {
|
participant: {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
||||||
|
|
@ -25,25 +25,11 @@ export async function loader({ params }: Route.LoaderArgs) {
|
||||||
|
|
||||||
const events = await getScoringEventsForSportsSeason(params.id);
|
const events = await getScoringEventsForSportsSeason(params.id);
|
||||||
|
|
||||||
// For qualifying sports seasons, get QP standings with global ranks attached
|
// For qualifying sports seasons, get QP standings
|
||||||
let qpStandings = null;
|
let qpStandings = null;
|
||||||
const scoringRules = null;
|
const scoringRules = null;
|
||||||
if (sportsSeason.scoringPattern === "qualifying_points") {
|
if (sportsSeason.scoringPattern === "qualifying_points") {
|
||||||
const standings = await getQPStandings(params.id);
|
qpStandings = await getQPStandings(params.id);
|
||||||
let prevQP = -1;
|
|
||||||
let prevRankStart = 1;
|
|
||||||
qpStandings = standings.map((s, index) => {
|
|
||||||
const qp = parseFloat(s.totalQualifyingPoints);
|
|
||||||
let rank: number;
|
|
||||||
if (index > 0 && Math.abs(qp - prevQP) < 0.001) {
|
|
||||||
rank = prevRankStart;
|
|
||||||
} else {
|
|
||||||
rank = index + 1;
|
|
||||||
prevRankStart = rank;
|
|
||||||
}
|
|
||||||
prevQP = qp;
|
|
||||||
return { ...s, globalRank: rank };
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tournaments for this sport that don't already have a scoring event in this season.
|
// Tournaments for this sport that don't already have a scoring event in this season.
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ export async function loader(args: Route.LoaderArgs) {
|
||||||
let participantPoints: { participantId: string; points: number }[] = [];
|
let participantPoints: { participantId: string; points: number }[] = [];
|
||||||
let partialScoreParticipantIds: string[] = [];
|
let partialScoreParticipantIds: string[] = [];
|
||||||
let seasonStandings: SeasonStanding[] = [];
|
let seasonStandings: SeasonStanding[] = [];
|
||||||
type QPStanding = Awaited<ReturnType<typeof getQPStandings>>[number] & { globalRank: number };
|
type QPStanding = Awaited<ReturnType<typeof getQPStandings>>[number];
|
||||||
let qpStandings: QPStanding[] = [];
|
let qpStandings: QPStanding[] = [];
|
||||||
|
|
||||||
// Group standings for group-stage events (e.g. FIFA World Cup)
|
// Group standings for group-stage events (e.g. FIFA World Cup)
|
||||||
|
|
@ -293,28 +293,9 @@ export async function loader(args: Route.LoaderArgs) {
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
} else if (scoringPattern === "qualifying_points") {
|
} else if (scoringPattern === "qualifying_points") {
|
||||||
|
// Fetch qualifying points standings
|
||||||
const standings = await getQPStandings(sportsSeasonId);
|
const standings = await getQPStandings(sportsSeasonId);
|
||||||
// Compute global ranks with tie handling across the full field before filtering,
|
qpStandings = standings;
|
||||||
// so the displayed rank numbers remain correct after undrafted/zero-QP rows are removed.
|
|
||||||
let prevQP = -1;
|
|
||||||
let prevRankStart = 1;
|
|
||||||
const withGlobalRank = standings.map((s, index) => {
|
|
||||||
const qp = parseFloat(s.totalQualifyingPoints);
|
|
||||||
let rank: number;
|
|
||||||
if (index > 0 && Math.abs(qp - prevQP) < 0.001) {
|
|
||||||
rank = prevRankStart;
|
|
||||||
} else {
|
|
||||||
rank = index + 1;
|
|
||||||
prevRankStart = rank;
|
|
||||||
}
|
|
||||||
prevQP = qp;
|
|
||||||
return { ...s, globalRank: rank };
|
|
||||||
});
|
|
||||||
qpStandings = withGlobalRank.filter(
|
|
||||||
(s) =>
|
|
||||||
parseFloat(s.totalQualifyingPoints) > 0 ||
|
|
||||||
ownershipMap.has(s.participant.id)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch event schedule for all patterns (upcoming + recent)
|
// Fetch event schedule for all patterns (upcoming + recent)
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ describe("WorldCupSimulator", () => {
|
||||||
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
|
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
|
||||||
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
|
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
|
||||||
|
|
||||||
const sim = new WorldCupSimulator(20);
|
const sim = new WorldCupSimulator(100);
|
||||||
await expect(sim.simulate("season-1")).rejects.toThrow("No participants found");
|
await expect(sim.simulate("season-1")).rejects.toThrow("No participants found");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -125,7 +125,7 @@ describe("WorldCupSimulator", () => {
|
||||||
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
|
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
|
||||||
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
|
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
|
||||||
|
|
||||||
const sim = new WorldCupSimulator(20);
|
const sim = new WorldCupSimulator(100);
|
||||||
const results = await sim.simulate("season-1");
|
const results = await sim.simulate("season-1");
|
||||||
expect(results).toHaveLength(48);
|
expect(results).toHaveLength(48);
|
||||||
const ids = new Set(results.map((r) => r.participantId));
|
const ids = new Set(results.map((r) => r.participantId));
|
||||||
|
|
@ -141,7 +141,7 @@ describe("WorldCupSimulator", () => {
|
||||||
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
|
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
|
||||||
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
|
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
|
||||||
|
|
||||||
const sim = new WorldCupSimulator(20);
|
const sim = new WorldCupSimulator(100);
|
||||||
const results = await sim.simulate("season-1");
|
const results = await sim.simulate("season-1");
|
||||||
|
|
||||||
const sumFirst = results.reduce((s, r) => s + r.probabilities.probFirst, 0);
|
const sumFirst = results.reduce((s, r) => s + r.probabilities.probFirst, 0);
|
||||||
|
|
@ -162,7 +162,7 @@ describe("WorldCupSimulator", () => {
|
||||||
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
|
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
|
||||||
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
|
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
|
||||||
|
|
||||||
const sim = new WorldCupSimulator(50);
|
const sim = new WorldCupSimulator(100);
|
||||||
const results = await sim.simulate("season-1");
|
const results = await sim.simulate("season-1");
|
||||||
|
|
||||||
// probFirst + probSecond + probThird + probFourth should cover all probability mass
|
// probFirst + probSecond + probThird + probFourth should cover all probability mass
|
||||||
|
|
@ -218,7 +218,7 @@ describe("WorldCupSimulator", () => {
|
||||||
mockDb.query.tournamentGroups.findMany.mockResolvedValue([group, ...remainingGroups]);
|
mockDb.query.tournamentGroups.findMany.mockResolvedValue([group, ...remainingGroups]);
|
||||||
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
|
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
|
||||||
|
|
||||||
const sim = new WorldCupSimulator(20);
|
const sim = new WorldCupSimulator(100);
|
||||||
const results = await sim.simulate("season-1");
|
const results = await sim.simulate("season-1");
|
||||||
|
|
||||||
const p3Result = results.find((r) => r.participantId === "p3");
|
const p3Result = results.find((r) => r.participantId === "p3");
|
||||||
|
|
@ -238,7 +238,7 @@ describe("WorldCupSimulator", () => {
|
||||||
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
|
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
|
||||||
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
|
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
|
||||||
|
|
||||||
const sim = new WorldCupSimulator(20);
|
const sim = new WorldCupSimulator(100);
|
||||||
const results = await sim.simulate("season-1");
|
const results = await sim.simulate("season-1");
|
||||||
|
|
||||||
for (const r of results) {
|
for (const r of results) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue