diff --git a/app/db/tables.ts b/app/db/tables.ts index 044c3cbfd..8844bbd11 100644 --- a/app/db/tables.ts +++ b/app/db/tables.ts @@ -483,6 +483,8 @@ export interface Tournament { rules: string | null; /** Related "parent tournament", the tournament that contains the original sign-ups (for leagues) */ parentTournamentId: number | null; + /** Is the tournament finalized meaning all the matches are played and TO has locked it making it read-only */ + isFinalized: Generated; } export interface PreparedMaps { @@ -850,6 +852,8 @@ export interface User { noScreen: Generated; buildSorting: JSONColumnTypeNullable; preferences: JSONColumnTypeNullable; + /** User creation date. Can be null because we did not always save this. */ + createdAt: number | null; } /** Represents User joined with PlusTier table */ diff --git a/app/features/tournament-bracket/actions/to.$id.brackets.server.ts b/app/features/tournament-bracket/actions/to.$id.brackets.server.ts index 89dbfe4c5..77a4a86bb 100644 --- a/app/features/tournament-bracket/actions/to.$id.brackets.server.ts +++ b/app/features/tournament-bracket/actions/to.$id.brackets.server.ts @@ -34,7 +34,7 @@ import { import { getServerTournamentManager } from "../core/brackets-manager/manager.server"; import { roundMapsFromInput } from "../core/mapList.server"; import { tournamentSummary } from "../core/summarizer.server"; -import { addSummary } from "../queries/addSummary.server"; +import { addSummary, finalizeTournament } from "../queries/addSummary.server"; import { allMatchResultsByTournamentId } from "../queries/allMatchResultsByTournamentId.server"; import { bracketSchema } from "../tournament-bracket-schemas.server"; import { fillWithNullTillPowerOfTwo } from "../tournament-bracket-utils"; @@ -260,16 +260,19 @@ export const action: ActionFunction = async ({ params, request }) => { seedingSkillCountsFor, }); - logger.info( - `Inserting tournament summary. Tournament id: ${tournamentId}, mapResultDeltas.lenght: ${summary.mapResultDeltas.length}, playerResultDeltas.length ${summary.playerResultDeltas.length}, tournamentResults.length ${summary.tournamentResults.length}, skills.length ${summary.skills.length}, seedingSkills.length ${summary.seedingSkills.length}`, - ); - + const tournamentSummaryString = `Tournament id: ${tournamentId}, mapResultDeltas.lenght: ${summary.mapResultDeltas.length}, playerResultDeltas.length ${summary.playerResultDeltas.length}, tournamentResults.length ${summary.tournamentResults.length}, skills.length ${summary.skills.length}, seedingSkills.length ${summary.seedingSkills.length}`; if (!tournament.isTest) { + logger.info(`Inserting tournament summary. ${tournamentSummaryString}`); addSummary({ tournamentId, summary, season, }); + } else { + logger.info( + `Did not insert tournament summary. ${tournamentSummaryString}`, + ); + finalizeTournament(tournamentId); } if (tournament.ranked) { diff --git a/app/features/tournament-bracket/queries/addSummary.server.ts b/app/features/tournament-bracket/queries/addSummary.server.ts index bd51c4a32..5bfa04306 100644 --- a/app/features/tournament-bracket/queries/addSummary.server.ts +++ b/app/features/tournament-bracket/queries/addSummary.server.ts @@ -120,6 +120,12 @@ const addTournamentResultStm = sql.prepare(/* sql */ ` ) `); +const finalizeTournamentStm = sql.prepare(/* sql */ ` + update "Tournament" set + "isFinalized" = 1 + where "id" = @tournamentId +`); + export const addSummary = sql.transaction( ({ tournamentId, @@ -195,5 +201,10 @@ export const addSummary = sql.transaction( tournamentTeamId: tournamentResult.tournamentTeamId, }); } + + finalizeTournamentStm.run({ tournamentId }); }, ); + +export const finalizeTournament = (tournamentId: number) => + finalizeTournamentStm.run({ tournamentId }); diff --git a/app/features/tournament/TournamentRepository.server.ts b/app/features/tournament/TournamentRepository.server.ts index d22899927..9885043d2 100644 --- a/app/features/tournament/TournamentRepository.server.ts +++ b/app/features/tournament/TournamentRepository.server.ts @@ -36,7 +36,7 @@ export async function findById(id: number) { "CalendarEvent.id", "CalendarEventDate.eventId", ) - .select(({ eb, exists, selectFrom }) => [ + .select(({ eb }) => [ "Tournament.id", "CalendarEvent.id as eventId", "CalendarEvent.discordUrl", @@ -50,6 +50,7 @@ export async function findById(id: number) { "CalendarEvent.name", "CalendarEvent.description", "CalendarEventDate.startTime", + "Tournament.isFinalized", jsonObjectFrom( eb .selectFrom("TournamentOrganization") @@ -149,11 +150,6 @@ export async function findById(id: number) { "Tournament.id", ), ).as("bracketProgressionOverrides"), - exists( - selectFrom("TournamentResult") - .where("TournamentResult.tournamentId", "=", id) - .select("TournamentResult.tournamentId"), - ).as("isFinalized"), jsonArrayFrom( eb .selectFrom("TournamentTeam") diff --git a/app/features/user-page/UserRepository.server.ts b/app/features/user-page/UserRepository.server.ts index d81e67a22..529f793d2 100644 --- a/app/features/user-page/UserRepository.server.ts +++ b/app/features/user-page/UserRepository.server.ts @@ -13,7 +13,7 @@ import type { import type { ChatUser } from "~/features/chat/components/Chat"; import { userRoles } from "~/modules/permissions/mapper.server"; import { isSupporter } from "~/modules/permissions/utils"; -import { dateToDatabaseTimestamp } from "~/utils/dates"; +import { databaseTimestampNow, dateToDatabaseTimestamp } from "~/utils/dates"; import invariant from "~/utils/invariant"; import type { CommonUser } from "~/utils/kysely.server"; import { COMMON_USER_FIELDS, userChatNameColor } from "~/utils/kysely.server"; @@ -661,7 +661,9 @@ export function upsert( .onConflict((oc) => { const { discordId, ...rest } = args; - return oc.column("discordId").doUpdateSet(rest); + return oc + .column("discordId") + .doUpdateSet({ ...rest, createdAt: databaseTimestampNow() }); }) .returning("id") .executeTakeFirstOrThrow(); diff --git a/migrations/087-finalize-tournament-bool.js b/migrations/087-finalize-tournament-bool.js new file mode 100644 index 000000000..d7dfcdfc4 --- /dev/null +++ b/migrations/087-finalize-tournament-bool.js @@ -0,0 +1,25 @@ +export function up(db) { + db.transaction(() => { + db.prepare(/* sql */ `alter table "User" add "createdAt" integer`).run(); + + db.prepare( + /* sql */ `alter table "Tournament" add "isFinalized" integer not null default 0`, + ).run(); + + const finalizedTournamentIds = db + .prepare( + /* sql */ `select "tournamentId" from "TournamentResult" group by "tournamentId"`, + ) + .all(); + + const updateStatement = db.prepare( + /* sql */ `update "Tournament" set isFinalized = 1 where id = @tournamentId`, + ); + + for (const tournamentId of finalizedTournamentIds) { + updateStatement.run({ + tournamentId: tournamentId.tournamentId, + }); + } + })(); +}