From 8fb4f515adbf9b22091fbbc4ee206d8c6c62ed00 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sun, 7 Aug 2022 15:36:10 +0300 Subject: [PATCH] Adjust calendar event results table to deduplicate --- TODO.md | 1 + app/db/models/calendar.server.ts | 95 +++++++++++++++------- app/db/seed.ts | 16 ++-- app/db/types.ts | 9 +- app/routes/calendar/$id/report-winners.tsx | 16 ++-- migrations/006-calendar.js | 21 +++-- 6 files changed, 109 insertions(+), 49 deletions(-) diff --git a/TODO.md b/TODO.md index 027e0988b..cf299de2d 100644 --- a/TODO.md +++ b/TODO.md @@ -23,6 +23,7 @@ Calendar ## Other +- [ ] Redirect to event page after submitting results - [x] date input take in account time zone difference between server and client - [x] User selector allow passing users from top level - [x] Constants use CALENDAR_EVENT object diff --git a/app/db/models/calendar.server.ts b/app/db/models/calendar.server.ts index a7b89b619..9698abbd4 100644 --- a/app/db/models/calendar.server.ts +++ b/app/db/models/calendar.server.ts @@ -7,7 +7,8 @@ import type { Badge, CalendarEventTag, CalendarEventBadge, - CalendarEventWinner, + CalendarEventResultTeam, + CalendarEventResultPlayer, } from "../types"; const createStm = sql.prepare(` @@ -137,72 +138,108 @@ const updateCalendarEventParticipantsCountStm = sql.prepare(` where "id" = $eventId `); -const deleteCalendarEventWinnersByEventIdStm = sql.prepare(` - delete from "CalendarEventWinner" +const deleteCalendarEventResultTeamsByEventIdStm = sql.prepare(` + delete from "CalendarEventResultTeam" where "eventId" = $eventId `); -const insertCalendarEventWinnerStm = sql.prepare(` - insert into "CalendarEventWinner" ( +const insertCalendarEventResultTeamStm = sql.prepare(` + insert into "CalendarEventResultTeam" ( "eventId", - "teamName", - "placement", + "name", + "placement" + ) values ( + $eventId, + $name, + $placement + ) + returning * +`); + +const insertCalendarEventResultPlayerStm = sql.prepare(` + insert into "CalendarEventResultPlayer" ( + "teamId", "userId", "name" ) values ( - $eventId, - $teamName, - $placement, + $teamId, $userId, $name ) `); + export const upsertReportedScores = sql.transaction( ({ eventId, participantCount, - winners, + results, }: { eventId: CalendarEvent["id"]; participantCount: CalendarEvent["participantCount"]; - winners: Array< - Pick - >; + results: Array<{ + teamName: CalendarEventResultTeam["name"]; + placement: CalendarEventResultTeam["placement"]; + players: Array<{ + userId: CalendarEventResultPlayer["userId"]; + name: CalendarEventResultPlayer["name"]; + }>; + }>; }) => { updateCalendarEventParticipantsCountStm.run({ eventId, participantCount }); - deleteCalendarEventWinnersByEventIdStm.run({ eventId }); + deleteCalendarEventResultTeamsByEventIdStm.run({ eventId }); - for (const winner of winners) { - insertCalendarEventWinnerStm.run({ ...winner, eventId }); + for (const { players, ...teamArgs } of results) { + const teamInDb = insertCalendarEventResultTeamStm.get({ + eventId, + name: teamArgs.teamName, + placement: teamArgs.placement, + }) as CalendarEventResultTeam; + + for (const playerArgs of players) { + insertCalendarEventResultPlayerStm.run({ + teamId: teamInDb.id, + userId: playerArgs.userId, + name: playerArgs.name, + }); + } } } ); const findWinnersByEventIdStm = sql.prepare(` select - "teamName", - "placement", - "userId", - "name" - from "CalendarEventWinner" - where "eventId" = $eventId + "CalendarEventResultTeam"."id", + "CalendarEventResultTeam"."name" as "teamName", + "CalendarEventResultTeam"."placement", + "CalendarEventResultPlayer"."userId" as "playerId", + "CalendarEventResultPlayer"."name" as "playerName" + from "CalendarEventResultTeam" + join + "CalendarEventResultPlayer" + on + "CalendarEventResultPlayer"."teamId" = "CalendarEventResultTeam"."id" + where "CalendarEventResultTeam"."eventId" = $eventId order by "placement" asc `); export function findWinnersByEventId(eventId: CalendarEvent["id"]) { - const rows = findWinnersByEventIdStm.all({ eventId }) as Array< - Pick - >; + const rows = findWinnersByEventIdStm.all({ eventId }) as Array<{ + id: CalendarEventResultTeam["id"]; + teamName: CalendarEventResultTeam["name"]; + placement: CalendarEventResultTeam["placement"]; + playerId: CalendarEventResultPlayer["userId"]; + playerName: CalendarEventResultPlayer["name"]; + }>; const result: Array<{ - teamName: CalendarEventWinner["teamName"]; - placement: CalendarEventWinner["placement"]; + teamName: CalendarEventResultTeam["name"]; + placement: CalendarEventResultTeam["placement"]; players: Array; }> = []; for (const row of rows) { const team = result.find((team) => team.teamName === row.teamName); - const player = row.name ?? { id: row.userId! }; + const player = row.playerName ?? { id: row.playerId! }; if (team) { team.players.push(player); diff --git a/app/db/seed.ts b/app/db/seed.ts index 835c0f04d..9a9505c12 100644 --- a/app/db/seed.ts +++ b/app/db/seed.ts @@ -19,7 +19,7 @@ const NZAP_TEST_DISCORD_ID = "455039198672453645"; const NZAP_TEST_AVATAR = "f809176af93132c3db5f0a5019e96339"; // https://cdn.discordapp.com/avatars/455039198672453645/f809176af93132c3db5f0a5019e96339.webp?size=160 const NZAP_TEST_ID = 2; -const AMOUNT_OF_CALENDAR_EVENTS = 100; +const AMOUNT_OF_CALENDAR_EVENTS = 200; const basicSeeds = [ adminUser, @@ -48,7 +48,8 @@ export function seed() { function wipeDB() { const tablesToDelete = [ "CalendarEventDate", - "CalendarEventWinner", + "CalendarEventResultPlayer", + "CalendarEventResultTeam", "CalendarEventBadge", "CalendarEvent", "User", @@ -315,11 +316,15 @@ function patrons() { } } -function calendarEvents() { - const userIds = sql +function userIdsInRandomOrder() { + return sql .prepare(`select "id" from "User" order by random()`) .all() .map((u) => u.id); +} + +function calendarEvents() { + const userIds = userIdsInRandomOrder(); for (let id = 1; id <= AMOUNT_OF_CALENDAR_EVENTS; id++) { const tags = shuffle(Object.keys(allTags)).filter((tag) => tag !== "BADGE"); @@ -369,7 +374,8 @@ function calendarEvents() { }); const twoDayEvent = Math.random() > 0.9; - const startTime = faker.date.soon(42); + const startTime = + id % 2 === 0 ? faker.date.soon(42) : faker.date.recent(42); startTime.setMinutes(0, 0, 0); sql diff --git a/app/db/types.ts b/app/db/types.ts index 65430bdb7..829a3a2fa 100644 --- a/app/db/types.ts +++ b/app/db/types.ts @@ -100,10 +100,15 @@ export interface CalendarEventDate { startTime: number; } -export interface CalendarEventWinner { +export interface CalendarEventResultTeam { + id: number; eventId: number; - teamName: string; + name: string; placement: number; +} + +export interface CalendarEventResultPlayer { + teamId: number; userId: number | null; name: string | null; } diff --git a/app/routes/calendar/$id/report-winners.tsx b/app/routes/calendar/$id/report-winners.tsx index 6c5322399..4ac3cb892 100644 --- a/app/routes/calendar/$id/report-winners.tsx +++ b/app/routes/calendar/$id/report-winners.tsx @@ -104,14 +104,14 @@ export const action: ActionFunction = async ({ request, params }) => { db.calendarEvents.upsertReportedScores({ eventId: parsedParams.id, participantCount: parsedInput.data.participantCount, - winners: parsedInput.data.team.flatMap((t) => - t.players.map((player) => ({ - teamName: t.teamName, - placement: t.placement, - userId: typeof player !== "string" ? player.id : null, - name: typeof player === "string" ? player : null, - })) - ), + results: parsedInput.data.team.map((t) => ({ + teamName: t.teamName, + placement: t.placement, + players: t.players.map((p) => ({ + userId: typeof p === "string" ? null : p.id, + name: typeof p === "string" ? p : null, + })), + })), }); return null; diff --git a/migrations/006-calendar.js b/migrations/006-calendar.js index 2f2be5d9a..9f6d2ea7b 100644 --- a/migrations/006-calendar.js +++ b/migrations/006-calendar.js @@ -29,14 +29,24 @@ module.exports.up = function (db) { db.prepare( ` - create table "CalendarEventWinner" ( + create table "CalendarEventResultTeam" ( + "id" integer primary key, "eventId" integer not null, - "teamName" text not null, + "name" text not null, "placement" integer not null, + foreign key ("eventId") references "CalendarEvent"("id") on delete cascade + ) strict + ` + ).run(); + + db.prepare( + ` + create table "CalendarEventResultPlayer" ( + "teamId" integer not null, "userId" integer, "name" text, - foreign key ("eventId") references "CalendarEvent"("id") on delete cascade, - foreign key ("userId") references "User"("id") on delete cascade + foreign key ("teamId") references "CalendarEventResultTeam"("id") on delete cascade, + foreign key ("userId") references "User"("id") on delete restrict ) strict ` ).run(); @@ -57,7 +67,8 @@ module.exports.up = function (db) { module.exports.down = function (db) { for (const table of [ "CalendarEventDate", - "CalendarEventWinner", + "CalendarEventResultPlayer", + "CalendarEventResultTeam", "CalendarEventBadge", "CalendarEvent", ]) {