From f460d52a4039ab85289afe5200225829aae8a0bd Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sun, 15 Feb 2026 21:23:13 +0200 Subject: [PATCH] Remove dead code, migrate one Skill function to Kysely (#2785) --- AGENTS.md | 4 +- app/features/mmr/SkillRepository.server.ts | 40 +++++++++++++++++++ app/features/mmr/mmr-utils.server.ts | 2 +- .../mmr/queries/findSeedingSkill.server.ts | 2 +- .../mmr/queries/orderedMMRBySeason.server.ts | 34 ++-------------- .../queries/seasonAllMMRByUserId.server.ts | 36 ----------------- app/features/mmr/tiered.server.ts | 7 +--- .../loaders/u.$identifier.seasons.server.ts | 7 +++- 8 files changed, 54 insertions(+), 78 deletions(-) create mode 100644 app/features/mmr/SkillRepository.server.ts delete mode 100644 app/features/mmr/queries/seasonAllMMRByUserId.server.ts diff --git a/AGENTS.md b/AGENTS.md index d2736c24e..62a2f34bf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,7 +3,7 @@ - only rarely use comments, prefer descriptive variable and function names (leave existing comments as is) - if you encounter an existing TODO comment assume it is there for a reason and do not remove it - task is not considered completely until `npm run checks` passes -- normal file structure has constants at the top immediately followed by the main function body of the file. Helpers are used to structure the code and they are at the bottom of the file (then hoisted to the top) +- normal file structure has constants at the top immediately followed by the main function body of the file. Helpers are used to structure the code and they are at the bottom of the file (main implementation first, at the top of the file) - note: any formatting issue (such as tabs vs. spaces) can be resolved by running the `npm run biome:fix` command ## Commands @@ -17,7 +17,7 @@ ## Typescript -- prefer early return over nesting if statements +- prefer early return over nesting if statements (bouncer pattern) - do not use `any` type - for constants use ALL_CAPS - always use named exports diff --git a/app/features/mmr/SkillRepository.server.ts b/app/features/mmr/SkillRepository.server.ts new file mode 100644 index 000000000..e4b746961 --- /dev/null +++ b/app/features/mmr/SkillRepository.server.ts @@ -0,0 +1,40 @@ +import { sql } from "kysely"; +import { db } from "~/db/sql"; +import { MATCHES_COUNT_NEEDED_FOR_LEADERBOARD } from "../leaderboards/leaderboards-constants"; + +export async function seasonProgressionByUserId({ + userId, + season, +}: { + userId: number; + season: number; +}) { + return db + .selectFrom("Skill") + .leftJoin("GroupMatch", "GroupMatch.id", "Skill.groupMatchId") + .leftJoin("Tournament", "Tournament.id", "Skill.tournamentId") + .leftJoin("CalendarEvent", "Tournament.id", "CalendarEvent.tournamentId") + .leftJoin( + "CalendarEventDate", + "CalendarEvent.id", + "CalendarEventDate.eventId", + ) + .select(({ fn }) => [ + fn.max("Skill.ordinal").as("ordinal"), + sql`date(coalesce("GroupMatch"."createdAt", "CalendarEventDate"."startTime"), 'unixepoch')`.as( + "date", + ), + ]) + .where("Skill.userId", "=", userId) + .where("Skill.season", "=", season) + .where("Skill.matchesCount", ">=", MATCHES_COUNT_NEEDED_FOR_LEADERBOARD) + .where(({ or, eb }) => + or([ + eb("GroupMatch.id", "is not", null), + eb("Tournament.id", "is not", null), + ]), + ) + .groupBy("date") + .orderBy("date", "asc") + .execute(); +} diff --git a/app/features/mmr/mmr-utils.server.ts b/app/features/mmr/mmr-utils.server.ts index 4903b4b27..112d9fadb 100644 --- a/app/features/mmr/mmr-utils.server.ts +++ b/app/features/mmr/mmr-utils.server.ts @@ -12,7 +12,7 @@ export function queryCurrentUserRating({ userId: number; season: number; }) { - const skill = findCurrentSkillByUserId({ userId, season: season ?? null }); + const skill = findCurrentSkillByUserId({ userId, season }); if (!skill) { return { rating: rating(), matchesCount: 0 }; diff --git a/app/features/mmr/queries/findSeedingSkill.server.ts b/app/features/mmr/queries/findSeedingSkill.server.ts index 6367adb1c..cf374ff50 100644 --- a/app/features/mmr/queries/findSeedingSkill.server.ts +++ b/app/features/mmr/queries/findSeedingSkill.server.ts @@ -1,5 +1,5 @@ import { sql } from "~/db/sql"; -import type { Tables } from "../../../db/tables"; +import type { Tables } from "~/db/tables"; const stm = sql.prepare(/* sql */ ` select diff --git a/app/features/mmr/queries/orderedMMRBySeason.server.ts b/app/features/mmr/queries/orderedMMRBySeason.server.ts index 4f008b119..cdd927836 100644 --- a/app/features/mmr/queries/orderedMMRBySeason.server.ts +++ b/app/features/mmr/queries/orderedMMRBySeason.server.ts @@ -21,36 +21,8 @@ const userStm = sql.prepare(/* sql */ ` "Skill"."ordinal" desc `); -const teamStm = sql.prepare(/* sql */ ` - select - "Skill"."ordinal", - "Skill"."matchesCount", - "Skill"."identifier" - from - "Skill" - inner join ( - select "identifier", max("id") as "maxId" - from "Skill" - where "Skill"."season" = @season - group by "identifier" - ) "Latest" on "Skill"."identifier" = "Latest"."identifier" and "Skill"."id" = "Latest"."maxId" - where - "Skill"."season" = @season - and "Skill"."identifier" is not null - order by - "Skill"."ordinal" desc -`); - -export function orderedMMRBySeason({ - season, - type, -}: { - season: number; - type: "team" | "user"; -}) { - const stm = type === "team" ? teamStm : userStm; - - return stm.all({ season }) as Array< - Pick +export function orderedUserMMRBySeason(season: number) { + return userStm.all({ season }) as Array< + Pick >; } diff --git a/app/features/mmr/queries/seasonAllMMRByUserId.server.ts b/app/features/mmr/queries/seasonAllMMRByUserId.server.ts deleted file mode 100644 index e609104a0..000000000 --- a/app/features/mmr/queries/seasonAllMMRByUserId.server.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { sql } from "~/db/sql"; -import { MATCHES_COUNT_NEEDED_FOR_LEADERBOARD } from "~/features/leaderboards/leaderboards-constants"; - -const groupedSkillsStm = sql.prepare(/* sql */ ` - select - max("Skill"."ordinal") as "ordinal", - date( - coalesce("GroupMatch"."createdAt", "CalendarEventDate"."startTime"), 'unixepoch' - ) as "date" - from - "Skill" - left join "GroupMatch" on "GroupMatch"."id" = "Skill"."groupMatchId" - left join "Tournament" on "Tournament"."id" = "Skill"."tournamentId" - left join "CalendarEvent" on "Tournament"."id" = "CalendarEvent"."tournamentId" - left join "CalendarEventDate" on "CalendarEvent"."id" = "CalendarEventDate"."eventId" - where - "Skill"."userId" = @userId - and "Skill"."season" = @season - and "Skill"."matchesCount" >= ${MATCHES_COUNT_NEEDED_FOR_LEADERBOARD} - and ("GroupMatch"."id" is not null or "Tournament"."id" is not null) - group by "date" - order by "date" asc -`); - -export function seasonAllMMRByUserId({ - userId, - season, -}: { - userId: number; - season: number; -}) { - return groupedSkillsStm.all({ userId, season }) as Array<{ - ordinal: number; - date: string; - }>; -} diff --git a/app/features/mmr/tiered.server.ts b/app/features/mmr/tiered.server.ts index 1c26ddb59..d6fd18fd2 100644 --- a/app/features/mmr/tiered.server.ts +++ b/app/features/mmr/tiered.server.ts @@ -9,7 +9,7 @@ import { type TierName, USER_LEADERBOARD_MIN_ENTRIES_FOR_LEVIATHAN, } from "./mmr-constants"; -import { orderedMMRBySeason } from "./queries/orderedMMRBySeason.server"; +import { orderedUserMMRBySeason } from "./queries/orderedMMRBySeason.server"; export interface TieredSkill { ordinal: number; @@ -25,10 +25,7 @@ export function freshUserSkills(season: number): { intervals: SkillTierInterval[]; isAccurateTiers: boolean; } { - const points = orderedMMRBySeason({ - season, - type: "user", - }); + const points = orderedUserMMRBySeason(season); const { intervals, isAccurateTiers } = skillTierIntervals(points, "user"); diff --git a/app/features/user-page/loaders/u.$identifier.seasons.server.ts b/app/features/user-page/loaders/u.$identifier.seasons.server.ts index ccb8e729a..bf92700b1 100644 --- a/app/features/user-page/loaders/u.$identifier.seasons.server.ts +++ b/app/features/user-page/loaders/u.$identifier.seasons.server.ts @@ -1,7 +1,7 @@ import type { LoaderFunctionArgs } from "react-router"; import { getUser } from "~/features/auth/core/user.server"; import * as LeaderboardRepository from "~/features/leaderboards/LeaderboardRepository.server"; -import { seasonAllMMRByUserId } from "~/features/mmr/queries/seasonAllMMRByUserId.server"; +import * as SkillRepository from "~/features/mmr/SkillRepository.server"; import { userSkills as _userSkills } from "~/features/mmr/tiered.server"; import { seasonMapWinrateByUserId } from "~/features/sendouq/queries/seasonMapWinrateByUserId.server"; import { seasonReportedWeaponsByUserId } from "~/features/sendouq/queries/seasonReportedWeaponsByUserId.server"; @@ -58,7 +58,10 @@ export const loader = async ({ params, request }: LoaderFunctionArgs) => { maps: seasonMapWinrateByUserId({ season, userId: user.id }), sets: seasonSetWinrateByUserId({ season, userId: user.id }), }, - skills: seasonAllMMRByUserId({ season, userId: user.id }), + skills: await SkillRepository.seasonProgressionByUserId({ + season, + userId: user.id, + }), tier, isAccurateTiers, results: {