From 8a773650fdaf63c026b02c58785f2d5c4f289145 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Fri, 1 May 2026 15:51:40 +0300 Subject: [PATCH] Don't allow too old tournaments to contribute to already closed seasonal rankings --- .../to.$id.brackets.finalize.server.ts | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/app/features/tournament-bracket/actions/to.$id.brackets.finalize.server.ts b/app/features/tournament-bracket/actions/to.$id.brackets.finalize.server.ts index cbd3e1cba..2047778b5 100644 --- a/app/features/tournament-bracket/actions/to.$id.brackets.finalize.server.ts +++ b/app/features/tournament-bracket/actions/to.$id.brackets.finalize.server.ts @@ -1,3 +1,4 @@ +import { differenceInHours } from "date-fns"; import type { ActionFunctionArgs } from "react-router"; import { requireUser } from "~/features/auth/core/user.server"; import * as BadgeRepository from "~/features/badges/BadgeRepository.server"; @@ -65,10 +66,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => { const results = allMatchResultsByTournamentId(tournamentId); invariant(results.length > 0, "No results found"); - const seasonAttributionDate = tournament.isLeagueDivision - ? new Date() - : tournament.ctx.startTime; - const season = Seasons.current(seasonAttributionDate)?.nth; + const season = resolveFinalizationSeason(tournament); const seedingSkillCountsFor = tournament.skillCountsFor; const standingsResult = Standings.tournamentStandings(tournament); @@ -77,7 +75,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => { teams: tournament.ctx.teams, finalStandings, results, - calculateSeasonalStats: tournament.ranked, + calculateSeasonalStats: tournament.ranked && typeof season === "number", queryCurrentTeamRating: (identifier) => queryCurrentTeamRating({ identifier, season: season! }).rating, queryCurrentUserRating: (userId) => @@ -118,9 +116,9 @@ export const action = async ({ request, params }: ActionFunctionArgs) => { await updateSeriesTierHistory(tournament); } - if (tournament.ranked) { + if (tournament.ranked && typeof season === "number") { try { - refreshUserSkills(season!); + refreshUserSkills(season); } catch (error) { logger.warn("Error refreshing user skills", error); } @@ -214,3 +212,18 @@ async function updateSeriesTierHistory(tournament: Tournament) { logger.error("Error updating series tier history", error); } } + +function resolveFinalizationSeason(tournament: Tournament) { + // league divisions might be running for many weeks + const attributionDate = tournament.isLeagueDivision + ? new Date() + : tournament.ctx.startTime; + const season = Seasons.current(attributionDate); + if (!season) return undefined; + + // don't allow changing seasons that have already been closed for a long while + // even if you were sluggish with finalizing the tournament + if (differenceInHours(new Date(), season.ends) >= 24) return undefined; + + return season.nth; +}