diff --git a/.github/scripts/copy-prod-db/index.ts b/.github/scripts/copy-prod-db/index.ts index fca8a3537..d481f65d8 100644 --- a/.github/scripts/copy-prod-db/index.ts +++ b/.github/scripts/copy-prod-db/index.ts @@ -111,12 +111,21 @@ async function main() { -- Update DiscordClubMetadata for 'MHC++' UPDATE \"DiscordClubMetadata\" m - SET + SET \"guildId\" = '1389762654452580373', \"leaderboardChannelId\" = '1401739528057655436' FROM \"DiscordClub\" c WHERE c.\"id\" = m.\"discordClubId\" AND c.\"name\" = 'MHC++'; + + -- Update DiscordClubMetadata for 'GWC - Hunter College' + UPDATE \"DiscordClubMetadata\" m + SET + \"guildId\" = '1389762654452580373', + \"leaderboardChannelId\" = '1401739528057655436' + FROM \"DiscordClub\" c + WHERE c.\"id\" = m.\"discordClubId\" + AND c.\"name\" = 'GWC - Hunter College'; "`; await sendMessage(prId, `Database copy command completed successfully!`); diff --git a/db/migration/V0072__Add_gwc_hunter_to_discord_club.SQL b/db/migration/V0072__Add_gwc_hunter_to_discord_club.SQL new file mode 100644 index 000000000..0b1fc9d56 --- /dev/null +++ b/db/migration/V0072__Add_gwc_hunter_to_discord_club.SQL @@ -0,0 +1,46 @@ +INSERT INTO "DiscordClub" + (id, name, description, tag) +VALUES + (gen_random_uuid(), 'GWC - Hunter College', NULL, 'Gwc'); + + +DO $$ +BEGIN + CASE current_database() + WHEN 'codebloom-prod' THEN + WITH CLUB AS ( + SELECT + * + FROM + "DiscordClub" + WHERE + name = 'GWC - Hunter College' + ) + INSERT INTO "DiscordClubMetadata" + (id, "guildId", "leaderboardChannelId", "discordClubId") + SELECT + gen_random_uuid(), + '1066177903345278986', + '1468417251274129452', + CLUB.id + FROM CLUB; + + ELSE + WITH CLUB AS ( + SELECT + * + FROM + "DiscordClub" + WHERE + name = 'GWC - Hunter College' + ) + INSERT INTO "DiscordClubMetadata" + (id, "guildId", "leaderboardChannelId", "discordClubId") + SELECT + gen_random_uuid(), + '1389762654452580373', + '1463703700697518113', + CLUB.id + FROM CLUB; + END CASE; +END $$; diff --git a/js/src/lib/api/utils/metadata/tag/index.ts b/js/src/lib/api/utils/metadata/tag/index.ts index 5789642cf..722f7fc95 100644 --- a/js/src/lib/api/utils/metadata/tag/index.ts +++ b/js/src/lib/api/utils/metadata/tag/index.ts @@ -41,8 +41,8 @@ export const TAG_METADATA_LIST: Record = { alt: "Patina Logo", }, Gwc: { - shortName: "GWC @ Hunter", - name: "Hunter College - GWC", + shortName: "GWC - Hunter College", + name: "GWC - Hunter College", apiKey: "gwc", icon: "/brands/Gwc_Logo.png", alt: "GWC Logo", @@ -96,4 +96,5 @@ export const NON_SCHOOL_TAGS: Tag[] = [ ...UNUSED_TAGS, Tag.Patina, Tag.MHCPlusPlus, + Tag.Gwc, ]; diff --git a/src/main/java/org/patinanetwork/codebloom/common/components/LeaderboardManager.java b/src/main/java/org/patinanetwork/codebloom/common/components/LeaderboardManager.java index 675828e17..72ffa154d 100644 --- a/src/main/java/org/patinanetwork/codebloom/common/components/LeaderboardManager.java +++ b/src/main/java/org/patinanetwork/codebloom/common/components/LeaderboardManager.java @@ -67,10 +67,10 @@ public void generateAchievementsForAllWinners() { List winners = usersWithPoints.subList(0, maxWinners(usersWithPoints.size())); for (int i = 0; i < winners.size(); i++) { + UserWithScore user = winners.get(i); int place = i + 1; log.info("on leaderboard for {} for winner #{}", pair.getRight().getResolvedName(), place); String placeString = calculatePlaceString(place); - UserWithScore user = winners.get(i); Achievement achievement = Achievement.builder() .userId(user.getId()) .place(AchievementPlaceEnum.fromInteger(place)) diff --git a/src/main/java/org/patinanetwork/codebloom/common/db/models/usertag/Tag.java b/src/main/java/org/patinanetwork/codebloom/common/db/models/usertag/Tag.java index 1f7333ce7..c0f387914 100644 --- a/src/main/java/org/patinanetwork/codebloom/common/db/models/usertag/Tag.java +++ b/src/main/java/org/patinanetwork/codebloom/common/db/models/usertag/Tag.java @@ -14,7 +14,7 @@ public enum Tag { Nyu("New York University"), Baruch("Baruch College"), Rpi("Rensselaer Polytechnic Institute"), - Gwc(null), + Gwc("GWC - Hunter College"), Sbu("Stony Brook University"), Ccny("City College of New York"), Columbia("Columbia University"), diff --git a/src/main/java/org/patinanetwork/codebloom/common/db/repos/leaderboard/options/LeaderboardFilterGenerator.java b/src/main/java/org/patinanetwork/codebloom/common/db/repos/leaderboard/options/LeaderboardFilterGenerator.java index 172a1453e..89d83f890 100644 --- a/src/main/java/org/patinanetwork/codebloom/common/db/repos/leaderboard/options/LeaderboardFilterGenerator.java +++ b/src/main/java/org/patinanetwork/codebloom/common/db/repos/leaderboard/options/LeaderboardFilterGenerator.java @@ -22,7 +22,8 @@ public static List> generateAllSupportedTagT list.add(Pair.of(withOnlyTrue(opt -> opt.columbia(true)), Tag.Columbia)); list.add(Pair.of(withOnlyTrue(opt -> opt.cornell(true)), Tag.Cornell)); list.add(Pair.of(withOnlyTrue(opt -> opt.bmcc(true)), Tag.Bmcc)); - list.add(Pair.of(withOnlyTrue(opt -> opt.bmcc(true)), Tag.MHCPlusPlus)); + list.add(Pair.of(withOnlyTrue(opt -> opt.mhcplusplus(true)), Tag.MHCPlusPlus)); + list.add(Pair.of(withOnlyTrue(opt -> opt.gwc(true)), Tag.Gwc)); return list; } diff --git a/src/test/java/org/patinanetwork/codebloom/common/components/LeaderboardManagerTest.java b/src/test/java/org/patinanetwork/codebloom/common/components/LeaderboardManagerTest.java index 2f50b4d4a..e5fd649a2 100644 --- a/src/test/java/org/patinanetwork/codebloom/common/components/LeaderboardManagerTest.java +++ b/src/test/java/org/patinanetwork/codebloom/common/components/LeaderboardManagerTest.java @@ -365,11 +365,8 @@ void testWithAvailableLeaderboardAndTwoWinnersGlobalWithOneValidOneInvalidTag() leaderboardManager.generateAchievementsForAllWinners(); - verify(achievementRepository, times(0)) - .createAchievement(argThat(achievement -> achievement.getLeaderboard() == Tag.Gwc)); - ArgumentCaptor captor = ArgumentCaptor.forClass(Achievement.class); - verify(achievementRepository, times(winners.size() * 2)).createAchievement(captor.capture()); + verify(achievementRepository, times(winners.size() * 3)).createAchievement(captor.capture()); verify(leaderboardRepository, times(validLeaderboardTags)).getRankedIndexedLeaderboardUsersById(any(), any()); verify(leaderboardRepository, times(1)).getGlobalRankedIndexedLeaderboardUsersById(any(), any()); @@ -388,6 +385,82 @@ void testWithAvailableLeaderboardAndTwoWinnersGlobalWithOneValidOneInvalidTag() Tag.Sbu, AchievementPlaceEnum.TWO, winners.get(1).getItem().getId()); + var userOneGwcAchievement = achievements.get(2); + var userTwoGwcAchievement = achievements.get(3); + assertAchievement( + userOneGwcAchievement, + Tag.Gwc, + AchievementPlaceEnum.ONE, + winners.get(0).getItem().getId()); + assertAchievement( + userTwoGwcAchievement, + Tag.Gwc, + AchievementPlaceEnum.TWO, + winners.get(1).getItem().getId()); + var userOneGlobalAchievement = achievements.get(4); + var userTwoGlobalAchievement = achievements.get(5); + assertAchievement( + userOneGlobalAchievement, + null, + AchievementPlaceEnum.ONE, + winners.get(0).getItem().getId()); + assertAchievement( + userTwoGlobalAchievement, + null, + AchievementPlaceEnum.TWO, + winners.get(1).getItem().getId()); + } + + @Test + void testWithAvailableLeaderboardAndTwoWinnersWithOnlyGwcTag() { + var latestLeaderboard = Leaderboard.builder() + .id(UUID.randomUUID().toString()) + .name("Test Leaderboard") + .createdAt(StandardizedLocalDateTime.now()) + .build(); + + var winners = Indexed.ofDefaultList(List.of( + randomPartialUserWithScore().totalScore(150_000).build(), + randomPartialUserWithScore().totalScore(70_000).build())); + + winners.forEach(winner -> { + var user = winner.getItem(); + user.setTags(List.of(UserTag.builder() + .id(UUID.randomUUID().toString()) + .tag(Tag.Gwc) + .userId(user.getId()) + .build())); + }); + + when(leaderboardRepository.getRecentLeaderboardMetadata()).thenReturn(latestLeaderboard); + when(leaderboardRepository.getGlobalRankedIndexedLeaderboardUsersById(eq(latestLeaderboard.getId()), any())) + .thenReturn(winners); + + when(leaderboardRepository.getRankedIndexedLeaderboardUsersById( + eq(latestLeaderboard.getId()), argThat(opt -> opt.isGwc()))) + .thenReturn(winners); + + leaderboardManager.generateAchievementsForAllWinners(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Achievement.class); + verify(achievementRepository, times(winners.size() * 2)).createAchievement(captor.capture()); + + verify(leaderboardRepository, times(validLeaderboardTags)).getRankedIndexedLeaderboardUsersById(any(), any()); + verify(leaderboardRepository, times(1)).getGlobalRankedIndexedLeaderboardUsersById(any(), any()); + + var achievements = captor.getAllValues(); + var userOneGwcAchievement = achievements.get(0); + var userTwoGwcAchievement = achievements.get(1); + assertAchievement( + userOneGwcAchievement, + Tag.Gwc, + AchievementPlaceEnum.ONE, + winners.get(0).getItem().getId()); + assertAchievement( + userTwoGwcAchievement, + Tag.Gwc, + AchievementPlaceEnum.TWO, + winners.get(1).getItem().getId()); var userOneGlobalAchievement = achievements.get(2); var userTwoGlobalAchievement = achievements.get(3); assertAchievement( @@ -402,6 +475,127 @@ void testWithAvailableLeaderboardAndTwoWinnersGlobalWithOneValidOneInvalidTag() winners.get(1).getItem().getId()); } + @Test + void testWithAvailableLeaderboardAndTwoWinnersWithGwcAndMhcPlusPlusTags() { + var latestLeaderboard = Leaderboard.builder() + .id(UUID.randomUUID().toString()) + .name("Test Leaderboard") + .createdAt(StandardizedLocalDateTime.now()) + .build(); + + var winners = Indexed.ofDefaultList(List.of( + randomPartialUserWithScore().totalScore(150_000).build(), + randomPartialUserWithScore().totalScore(70_000).build())); + + winners.forEach(winner -> { + var user = winner.getItem(); + user.setTags(List.of( + UserTag.builder() + .id(UUID.randomUUID().toString()) + .tag(Tag.Gwc) + .userId(user.getId()) + .build(), + UserTag.builder() + .id(UUID.randomUUID().toString()) + .tag(Tag.MHCPlusPlus) + .userId(user.getId()) + .build())); + }); + + when(leaderboardRepository.getRecentLeaderboardMetadata()).thenReturn(latestLeaderboard); + when(leaderboardRepository.getGlobalRankedIndexedLeaderboardUsersById(eq(latestLeaderboard.getId()), any())) + .thenReturn(winners); + + when(leaderboardRepository.getRankedIndexedLeaderboardUsersById( + eq(latestLeaderboard.getId()), argThat(opt -> opt.isGwc()))) + .thenReturn(winners); + + when(leaderboardRepository.getRankedIndexedLeaderboardUsersById( + eq(latestLeaderboard.getId()), argThat(opt -> opt.isMhcplusplus()))) + .thenReturn(winners); + + leaderboardManager.generateAchievementsForAllWinners(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Achievement.class); + verify(achievementRepository, times(winners.size() * 3)).createAchievement(captor.capture()); + + verify(leaderboardRepository, times(validLeaderboardTags)).getRankedIndexedLeaderboardUsersById(any(), any()); + verify(leaderboardRepository, times(1)).getGlobalRankedIndexedLeaderboardUsersById(any(), any()); + + /** keep in mind the order of how we check filters is ordered. check LeaderboardFilterGenerator for order. */ + var achievements = captor.getAllValues(); + var userOneMhcPlusPlusAchievement = achievements.get(0); + var userTwoMhcPlusPlusAchievement = achievements.get(1); + assertAchievement( + userOneMhcPlusPlusAchievement, + Tag.MHCPlusPlus, + AchievementPlaceEnum.ONE, + winners.get(0).getItem().getId()); + assertAchievement( + userTwoMhcPlusPlusAchievement, + Tag.MHCPlusPlus, + AchievementPlaceEnum.TWO, + winners.get(1).getItem().getId()); + var userOneGwcAchievement = achievements.get(2); + var userTwoGwcAchievement = achievements.get(3); + assertAchievement( + userOneGwcAchievement, + Tag.Gwc, + AchievementPlaceEnum.ONE, + winners.get(0).getItem().getId()); + assertAchievement( + userTwoGwcAchievement, + Tag.Gwc, + AchievementPlaceEnum.TWO, + winners.get(1).getItem().getId()); + var userOneGlobalAchievement = achievements.get(4); + var userTwoGlobalAchievement = achievements.get(5); + assertAchievement( + userOneGlobalAchievement, + null, + AchievementPlaceEnum.ONE, + winners.get(0).getItem().getId()); + assertAchievement( + userTwoGlobalAchievement, + null, + AchievementPlaceEnum.TWO, + winners.get(1).getItem().getId()); + } + + @Test + void testGwcUsersWithZeroPointsAreExcludedFromTagAchievements() { + var latestLeaderboard = Leaderboard.builder() + .id(UUID.randomUUID().toString()) + .name("Test Leaderboard") + .createdAt(StandardizedLocalDateTime.now()) + .build(); + + var users = Indexed.ofDefaultList(List.of( + randomPartialUserWithScore().totalScore(0).build(), + randomPartialUserWithScore().totalScore(0).build())); + + users.forEach(winner -> { + var user = winner.getItem(); + user.setTags(List.of(UserTag.builder() + .id(UUID.randomUUID().toString()) + .tag(Tag.Gwc) + .userId(user.getId()) + .build())); + }); + + when(leaderboardRepository.getRecentLeaderboardMetadata()).thenReturn(latestLeaderboard); + when(leaderboardRepository.getGlobalRankedIndexedLeaderboardUsersById(eq(latestLeaderboard.getId()), any())) + .thenReturn(users); + + when(leaderboardRepository.getRankedIndexedLeaderboardUsersById( + eq(latestLeaderboard.getId()), argThat(opt -> opt.isGwc()))) + .thenReturn(users); + + leaderboardManager.generateAchievementsForAllWinners(); + + verify(achievementRepository, times(0)).createAchievement(any()); + } + @Test void testWithAvailableLeaderboardAndThreeWinnersGlobalButFourUsers() { var latestLeaderboard = Leaderboard.builder() @@ -509,6 +703,94 @@ void testUsersWithZeroPointsAreExcludedFromTagAchievements() { verify(achievementRepository, times(0)).createAchievement(any()); } + @Test + void testWinnersWithNullTagsAreSkippedInTagLeaderboards() { + var latestLeaderboard = Leaderboard.builder() + .id(UUID.randomUUID().toString()) + .name("Test Leaderboard") + .createdAt(StandardizedLocalDateTime.now()) + .build(); + + var winners = Indexed.ofDefaultList(List.of( + randomPartialUserWithScore().totalScore(150_000).build(), + randomPartialUserWithScore().totalScore(70_000).build())); + + when(leaderboardRepository.getRecentLeaderboardMetadata()).thenReturn(latestLeaderboard); + when(leaderboardRepository.getGlobalRankedIndexedLeaderboardUsersById(eq(latestLeaderboard.getId()), any())) + .thenReturn(winners); + + leaderboardManager.generateAchievementsForAllWinners(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Achievement.class); + verify(achievementRepository, times(winners.size())).createAchievement(captor.capture()); + + var achievements = captor.getAllValues(); + for (var achievement : achievements) { + assertNull(achievement.getLeaderboard()); + } + } + + @Test + void testWinnersWithNonMatchingTagsAreSkippedInTagLeaderboards() { + var latestLeaderboard = Leaderboard.builder() + .id(UUID.randomUUID().toString()) + .name("Test Leaderboard") + .createdAt(StandardizedLocalDateTime.now()) + .build(); + + var winners = Indexed.ofDefaultList(List.of( + randomPartialUserWithScore().totalScore(150_000).build(), + randomPartialUserWithScore().totalScore(70_000).build())); + + winners.forEach(winner -> { + var user = winner.getItem(); + user.setTags(List.of(UserTag.builder() + .id(UUID.randomUUID().toString()) + .tag(Tag.Patina) + .userId(user.getId()) + .build())); + }); + + when(leaderboardRepository.getRecentLeaderboardMetadata()).thenReturn(latestLeaderboard); + when(leaderboardRepository.getGlobalRankedIndexedLeaderboardUsersById(eq(latestLeaderboard.getId()), any())) + .thenReturn(winners); + + when(leaderboardRepository.getRankedIndexedLeaderboardUsersById( + eq(latestLeaderboard.getId()), argThat(opt -> opt.isPatina()))) + .thenReturn(winners); + + leaderboardManager.generateAchievementsForAllWinners(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Achievement.class); + verify(achievementRepository, times(winners.size() * 2)).createAchievement(captor.capture()); + + var achievements = captor.getAllValues(); + var userOnePatinaAchievement = achievements.get(0); + var userTwoPatinaAchievement = achievements.get(1); + assertAchievement( + userOnePatinaAchievement, + Tag.Patina, + AchievementPlaceEnum.ONE, + winners.get(0).getItem().getId()); + assertAchievement( + userTwoPatinaAchievement, + Tag.Patina, + AchievementPlaceEnum.TWO, + winners.get(1).getItem().getId()); + var userOneGlobalAchievement = achievements.get(2); + var userTwoGlobalAchievement = achievements.get(3); + assertAchievement( + userOneGlobalAchievement, + null, + AchievementPlaceEnum.ONE, + winners.get(0).getItem().getId()); + assertAchievement( + userTwoGlobalAchievement, + null, + AchievementPlaceEnum.TWO, + winners.get(1).getItem().getId()); + } + @Test void testGetLeaderboardMetadata() { String testId = UUID.randomUUID().toString(); diff --git a/src/test/java/org/patinanetwork/codebloom/common/db/repos/leaderboard/options/LeaderboardFilterGeneratorTest.java b/src/test/java/org/patinanetwork/codebloom/common/db/repos/leaderboard/options/LeaderboardFilterGeneratorTest.java index d68e901ed..c360a3c2d 100644 --- a/src/test/java/org/patinanetwork/codebloom/common/db/repos/leaderboard/options/LeaderboardFilterGeneratorTest.java +++ b/src/test/java/org/patinanetwork/codebloom/common/db/repos/leaderboard/options/LeaderboardFilterGeneratorTest.java @@ -22,7 +22,8 @@ public class LeaderboardFilterGeneratorTest { Tag.Hunter, Tag.Nyu, Tag.Rpi, - Tag.MHCPlusPlus); + Tag.MHCPlusPlus, + Tag.Gwc); public static final Set ALL_LEADERBOARD_TAGS = Set.of( Tag.Patina,