Exported images w/ SQ season summary downloadable (#3272)

This commit is contained in:
Kalle
2026-07-31 17:34:49 +03:00
committed by GitHub
parent 83660a5b2f
commit 97619bdc05
114 changed files with 5780 additions and 292 deletions

View File

@@ -163,34 +163,42 @@ export async function findSeasonProgressionByUserId({
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<string>`date(coalesce("Skill"."createdAt", "GroupMatch"."createdAt", "CalendarEventDate"."startsAt"), 'unixepoch')`.as(
"date",
return seasonSkillsByDayQuery({ userId, season })
.select(({ fn }) => fn.max("Skill.ordinal").as("ordinal"))
.where("Skill.matchesCount", ">=", MATCHES_COUNT_NEEDED_FOR_LEADERBOARD)
.execute();
}
/**
* Days of the season the user played at least one set on, with whether they
* played SendouQ, tournaments or both that day. Dates in `yyyy-MM-dd` format.
*/
export async function findSeasonActiveDaysByUserId({
userId,
season,
}: {
userId: number;
season: number;
}): Promise<Array<{ date: string; activity: "sq" | "tournament" | "both" }>> {
const rows = await seasonSkillsByDayQuery({ userId, season })
.select([
// raw max over a null check: did any of the day's Skill rows come from this source?
sql<number>`max("Skill"."groupMatchId" is not null)`.as("playedSq"),
sql<number>`max("Skill"."tournamentId" is not null)`.as(
"playedTournament",
),
])
.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();
return rows.map((row) => ({
date: row.date,
activity:
row.playedSq && row.playedTournament
? ("both" as const)
: row.playedSq
? ("sq" as const)
: ("tournament" as const),
}));
}
/**
@@ -216,3 +224,42 @@ function latestSkillsOfSeason(
])
.where("season", "=", season);
}
/**
* User's Skill rows of a season that came from a set played, grouped by the day
* it was played on (`yyyy-MM-dd`) in ascending order. Callers select what they
* want aggregated per day.
*/
function seasonSkillsByDayQuery({
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(
sql<string>`date(coalesce("Skill"."createdAt", "GroupMatch"."createdAt", "CalendarEventDate"."startsAt"), 'unixepoch')`.as(
"date",
),
)
.where("Skill.userId", "=", userId)
.where("Skill.season", "=", season)
.where(({ or, eb }) =>
or([
eb("GroupMatch.id", "is not", null),
eb("Tournament.id", "is not", null),
]),
)
.groupBy("date")
.orderBy("date", "asc");
}