Use Series.findByEventName everywhere a series is matched by event name

This commit is contained in:
Kalle
2026-11-08 14:37:54 +02:00
parent db5157f22d
commit be6281243b
6 changed files with 59 additions and 40 deletions

View File

@@ -33,6 +33,7 @@ import * as SQMatchRepository from "~/features/sendouq-match/SQMatchRepository.s
import * as SplatoonRotationRepository from "~/features/splatoon-rotations/SplatoonRotationRepository.server";
import { getViewerTimezone } from "~/features/timezone/timezone-context.server";
import { eventLeaderboards } from "~/features/tournament-organization/core/leaderboards.server";
import * as Series from "~/features/tournament-organization/core/Series";
import type {
BuildAbilitiesTuple,
MainWeaponId,
@@ -3467,13 +3468,12 @@ async function seriesWinOf(result: {
}) {
if (result.organizationId === null) return null;
const series = (
await FeedRepository.findSeriesByOrganizationId(result.organizationId)
).find((s) =>
s.substringMatches.some((sub) =>
result.name.toLowerCase().includes(sub.toLowerCase()),
const series = Series.findByEventName({
series: await FeedRepository.findSeriesByOrganizationId(
result.organizationId,
),
);
eventName: result.name,
});
if (!series) return null;
return {

View File

@@ -23,6 +23,7 @@ import {
} from "~/utils/kysely.server";
import { toDBBoolean } from "~/utils/sql";
import { mySlugify } from "~/utils/urls";
import * as Series from "./core/Series";
import { TOURNAMENT_SERIES_EVENTS_PER_PAGE } from "./tournament-organization-constants";
interface CreateArgs {
@@ -544,11 +545,7 @@ export async function findAllSeriesWinsByUserId({
eb.and([
eb("CalendarEvent.organizationId", "=", organizationId),
eb("CalendarEvent.hidden", "=", 0),
eb.or(
substringMatches.map((match) =>
eb("CalendarEvent.name", "like", `%${match}%`),
),
),
calendarEventNameMatchesSeries(substringMatches)(eb),
]);
const tournamentWins = await db
@@ -772,12 +769,9 @@ export function update({
for (const s of insertedSeries) {
const matchingTiers = finalizedTournaments
.filter((t) => {
const eventNameLower = t.name.toLowerCase();
return s.substringMatches.some((match) =>
eventNameLower.includes(match.toLowerCase()),
);
})
.filter((t) =>
Series.matchesEventName({ series: s, eventName: t.name }),
)
.filter((t) => t.tier !== null)
.map((t) => t.tier);
@@ -932,12 +926,7 @@ export async function updateSeriesTierHistory({
.where("organizationId", "=", organizationId)
.execute();
const eventNameLower = eventName.toLowerCase();
const matchingSeries = series.find((s) =>
s.substringMatches.some((match) =>
eventNameLower.includes(match.toLowerCase()),
),
);
const matchingSeries = Series.findByEventName({ series, eventName });
if (!matchingSeries) return;

View File

@@ -0,0 +1,18 @@
import { describe, expect, test } from "vitest";
import * as Series from "./Series";
describe("Series.findByEventName", () => {
const series = [
{ id: 1, substringMatches: ["low ink"] },
{ id: 2, substringMatches: ["Swim Or Sink", "sos"] },
];
test.each([
{ why: "lower-cased match", eventName: "Low Ink November", expected: 1 },
{ why: "mixed-case match", eventName: "swim or sink 42", expected: 2 },
{ why: "second substring", eventName: "SOS #12", expected: 2 },
{ why: "no match", eventName: "In The Zone 40", expected: undefined },
])("$why", ({ eventName, expected }) => {
expect(Series.findByEventName({ series, eventName })?.id).toBe(expected);
});
});

View File

@@ -6,11 +6,22 @@ export function findByEventName<T extends { substringMatches: string[] }>({
series: T[];
eventName: string;
}) {
const eventNameLower = eventName.toLowerCase();
return series.find((oneSeries) =>
oneSeries.substringMatches.some((substringMatch) =>
eventNameLower.includes(substringMatch.toLowerCase()),
),
matchesEventName({ series: oneSeries, eventName }),
);
}
/** Whether one of the series' substrings appears in the event's name. */
export function matchesEventName({
series,
eventName,
}: {
series: { substringMatches: string[] };
eventName: string;
}) {
const eventNameLower = eventName.toLowerCase();
return series.substringMatches.some((substringMatch) =>
eventNameLower.includes(substringMatch.toLowerCase()),
);
}

View File

@@ -4,6 +4,7 @@ import * as R from "remeda";
import { cache, ttl } from "~/utils/cache.server";
import { dateToDatabaseTimestamp } from "~/utils/dates";
import * as TournamentOrganizationRepository from "../TournamentOrganizationRepository.server";
import * as Series from "./Series";
const CACHE_KEY = "series-team-counts";
/** How old an edition can be and still say something about the next one. */
@@ -33,12 +34,10 @@ export async function lookup() {
const series = seriesByOrganizationId.get(tournament.organizationId);
if (!series) return tournament.teamCount;
const nameLower = tournament.name.toLowerCase();
const match = series.find((candidate) =>
candidate.substringMatches.some((substring) =>
nameLower.includes(substring.toLowerCase()),
),
);
const match = Series.findByEventName({
series,
eventName: tournament.name,
});
if (!match) return tournament.teamCount;
return Math.max(
@@ -71,9 +70,7 @@ async function seriesTeamCounts() {
.filter(
(tournament) =>
tournament.organizationId === row.organizationId &&
row.substringMatches.some((substring) =>
tournament.name.toLowerCase().includes(substring.toLowerCase()),
),
Series.matchesEventName({ series: row, eventName: tournament.name }),
)
.slice(-EDITIONS_CONSIDERED)
.map((tournament) => tournament.teamCount);

View File

@@ -4,6 +4,7 @@ import { calculateTentativeTier } from "~/features/tournament/core/tiering";
import * as TrophyRepository from "~/features/trophies/TrophyRepository.server";
import type { SerializeFrom } from "~/utils/remix";
import { eventLeaderboards } from "../core/leaderboards.server";
import * as Series from "../core/Series";
import * as TournamentOrganizationRepository from "../TournamentOrganizationRepository.server";
import { TOURNAMENT_SERIES_LEADERBOARD_SIZE } from "../tournament-organization-constants";
import { tournamentOrganizationSearchParams } from "../tournament-organization-search-params";
@@ -27,9 +28,12 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
const seriesId =
_seriesId ??
organization.series.find((s) =>
s.substringMatches.some((match) => source?.toLowerCase().includes(match)),
)?.id;
(source
? Series.findByEventName({
series: organization.series,
eventName: source,
})?.id
: undefined);
const seriesInfo = async () => {
const series = seriesId