Public Access
feat(stats): add PLATFORMS stat group and landing page statistics
Add PLATFORMS stat group to user stats: games and playtime per platform. Add platforms, genres, and finished-per-year statistics to public stats for the landing page.
This commit is contained in:
1 parent
216c1af62f
commit
732970697d
12 files changed
+244
-14
No files matched your search
@@ -81,7 +81,9 @@ class LibraryControllerStatsTest extends AbstractMockedLibraryServicesTest {
|
||||
.andExpect(jsonPath("$.topGames.mostPlayed[0].appid").value(620))
|
||||
.andExpect(jsonPath("$.taste.genres").isArray())
|
||||
.andExpect(jsonPath("$.habits.underOneHourGames").value(0))
|
||||
.andExpect(jsonPath("$.finished.finishedBeforeTracking").value(1));
|
||||
.andExpect(jsonPath("$.finished.finishedBeforeTracking").value(1))
|
||||
.andExpect(jsonPath("$.platforms.perPlatform[0].name").value("Steam"))
|
||||
.andExpect(jsonPath("$.platforms.perPlatform[0].games").value(2));
|
||||
verify(moodService).refreshListedInBackground(USER_ID);
|
||||
}
|
||||
|
||||
|
||||
@@ -228,6 +228,26 @@ class UserStatsRepositoryTest extends AbstractPersistenceTest {
|
||||
new UserStats.YearCount(thisYear, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void gamesPerPlatform_GroupsTrimmedIgnoringCase_LabelIsTheMostUsedSpelling() {
|
||||
steamGameRepository.saveAllAndFlush(List.of(
|
||||
new SteamGame(8001L, "A", null, Instant.now()), new SteamGame(8002L, "B", null, Instant.now()),
|
||||
new SteamGame(8003L, "C", null, Instant.now()), new SteamGame(8004L, "D", null, Instant.now())));
|
||||
Instant now = Instant.now();
|
||||
userGameRepository.saveAllAndFlush(List.of(
|
||||
game(220L, 100, GameStatus.BACKLOG, now), game(620L, 50, GameStatus.BACKLOG, now),
|
||||
UserGame.manual(USER_ID, 8001L, "PS5", GameStatus.BACKLOG, 10, now),
|
||||
UserGame.manual(USER_ID, 8002L, "PS5", GameStatus.BACKLOG, 20, now),
|
||||
UserGame.manual(USER_ID, 8003L, "ps5 ", GameStatus.BACKLOG, 30, now),
|
||||
UserGame.manual(USER_ID, 8004L, "Switch", GameStatus.BACKLOG, 5, now)));
|
||||
seedHiddenAndRemoved();
|
||||
|
||||
assertThat(repository.gamesPerPlatform(USER_ID)).containsExactly(
|
||||
new UserStats.TagCount("PS5", 3, 60),
|
||||
new UserStats.TagCount("Steam", 2, 150),
|
||||
new UserStats.TagCount("Switch", 1, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void finishedBeforeTracking_FinishedGamesThatCameInFinished() {
|
||||
userGameRepository.saveAllAndFlush(List.of(
|
||||
|
||||
@@ -58,6 +58,7 @@ class UserStatsServiceTest extends AbstractUnitTest {
|
||||
assertThat(stats.habits()).isNull();
|
||||
assertThat(stats.finished()).isNull();
|
||||
assertThat(stats.trend()).isNull();
|
||||
assertThat(stats.platforms()).isNull();
|
||||
verifyNoInteractions(userGameRepository, backlogSnapshotRepository);
|
||||
}
|
||||
|
||||
@@ -91,7 +92,7 @@ class UserStatsServiceTest extends AbstractUnitTest {
|
||||
void getStats_NoGroups_RunsNoQuery() {
|
||||
UserStatsDto stats = service.getStats(7L, Set.of());
|
||||
|
||||
assertThat(stats).isEqualTo(new UserStatsDto(null, null, null, null, null, null, null));
|
||||
assertThat(stats).isEqualTo(new UserStatsDto(null, null, null, null, null, null, null, null));
|
||||
verifyNoInteractions(userGameRepository, userStatsRepository, backlogSnapshotRepository);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,24 @@
|
||||
package core_service.modules.stats.repo;
|
||||
|
||||
import core_service.modules.library.domain.GameStatus;
|
||||
import core_service.modules.library.domain.SteamGame;
|
||||
import core_service.modules.library.domain.UserGame;
|
||||
import core_service.modules.library.repo.GameMoodsRepository;
|
||||
import core_service.modules.library.repo.SteamGameRepository;
|
||||
import core_service.modules.library.repo.UserGameRepository;
|
||||
import core_service.modules.stats.api.dto.PublicStatsDto;
|
||||
import core_service.modules.stats.service.PublicStatsService;
|
||||
import core_service.shared.test.AbstractPersistenceTest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -29,6 +36,9 @@ class StatsRepositoryTest extends AbstractPersistenceTest {
|
||||
@Autowired
|
||||
private UserGameRepository userGameRepository;
|
||||
|
||||
@Autowired
|
||||
private GameMoodsRepository gameMoodsRepository;
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbc;
|
||||
|
||||
@@ -62,19 +72,98 @@ class StatsRepositoryTest extends AbstractPersistenceTest {
|
||||
assertEquals(List.of(220L), appids(statsRepository.topPlayedGames(5)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void gamesPerPlatform_GroupsIgnoringCase_LeavesDeletedUsersOut() {
|
||||
steamGameRepository.saveAllAndFlush(List.of(new SteamGame(8001L, "A", null, Instant.now()),
|
||||
new SteamGame(8002L, "B", null, Instant.now()), new SteamGame(8003L, "C", null, Instant.now())));
|
||||
userGameRepository.saveAllAndFlush(List.of(
|
||||
UserGame.manual(1L, 8001L, "ps5", GameStatus.BACKLOG, 10, Instant.now()),
|
||||
UserGame.manual(2L, 8002L, "PS5", GameStatus.BACKLOG, 20, Instant.now()),
|
||||
UserGame.manual(3L, 8003L, "Switch", GameStatus.BACKLOG, 20, Instant.now())));
|
||||
jdbc.update("INSERT INTO deleted_user (user_id) VALUES (3)");
|
||||
|
||||
List<Map<String, Object>> rows = statsRepository.gamesPerPlatform();
|
||||
|
||||
assertEquals(List.of("PS5", "Steam"), rows.stream().map(row -> row.get("name")).toList());
|
||||
assertEquals(List.of(2L, 1L), rows.stream().map(row -> ((Number) row.get("games")).longValue()).toList());
|
||||
assertEquals(30L, ((Number) rows.get(0).get("minutes")).longValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void topGenres_OrderByPlaytime_LeaveHiddenGamesOut() {
|
||||
gameMoodsRepository.upsert(220L, new String[]{}, new String[]{"RPG", "Action"}, false, Instant.now());
|
||||
gameMoodsRepository.upsert(1000L, new String[]{}, new String[]{"Simulator"}, false, Instant.now());
|
||||
|
||||
List<Map<String, Object>> rows = statsRepository.topGenres(8);
|
||||
|
||||
assertEquals(List.of("Action", "RPG"), rows.stream().map(row -> row.get("name")).toList());
|
||||
assertEquals(300L, ((Number) rows.get(0).get("minutes")).longValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void finishedPerYear_CountsTheFirstMoveIntoFinished_EstimateWins() {
|
||||
steamGameRepository.saveAllAndFlush(List.of(new SteamGame(620L, "Portal 2", null, Instant.now()),
|
||||
new SteamGame(700L, "OJ", null, Instant.now())));
|
||||
UserGame estimated = new UserGame(1L, 620L, 0, Instant.now(), Instant.now());
|
||||
estimated.setFinishedAtEstimate(Instant.parse("2025-06-01T00:00:00Z"));
|
||||
userGameRepository.saveAllAndFlush(List.of(estimated, new UserGame(1L, 700L, 0, Instant.now(), Instant.now())));
|
||||
jdbc.update("UPDATE user_game SET status = 'FINISHED' WHERE user_id = 1 AND appid = 620");
|
||||
jdbc.update("UPDATE user_game SET status = 'FINISHED' WHERE user_id = 1 AND appid = 700");
|
||||
// 220 is saved as FINISHED without a move: no year.
|
||||
jdbc.update("UPDATE user_game SET status = 'FINISHED' WHERE user_id = 1 AND appid = 220");
|
||||
jdbc.update("DELETE FROM user_game_status_change WHERE appid = 220");
|
||||
|
||||
List<Map<String, Object>> rows = statsRepository.finishedPerYear("Europe/Berlin");
|
||||
|
||||
assertEquals(List.of(2025, LocalDate.now().getYear()),
|
||||
rows.stream().map(row -> ((Number) row.get("year")).intValue()).toList());
|
||||
assertEquals(List.of(1L, 1L), rows.stream().map(row -> ((Number) row.get("games")).longValue()).toList());
|
||||
}
|
||||
|
||||
private static List<Long> appids(List<Map<String, Object>> rows) {
|
||||
return rows.stream().map(row -> ((Number) row.get("appid")).longValue()).toList();
|
||||
}
|
||||
|
||||
private void finishWithGenreForUser3() {
|
||||
jdbc.update("UPDATE user_game SET status = 'FINISHED' WHERE user_id = 3 AND appid = 620");
|
||||
gameMoodsRepository.upsert(620L, new String[]{}, new String[]{"Horror"}, false, Instant.now());
|
||||
}
|
||||
|
||||
private void assertLandingListsIgnoreUser3() {
|
||||
assertEquals(List.of(), statsRepository.topGenres(8));
|
||||
assertEquals(List.of(), statsRepository.finishedPerYear("Europe/Berlin"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void publicStats_ReadEveryRepositoryKey() {
|
||||
gameMoodsRepository.upsert(220L, new String[]{}, new String[]{"RPG"}, false, Instant.now());
|
||||
steamGameRepository.saveAndFlush(new SteamGame(620L, "Portal 2", null, Instant.now()));
|
||||
UserGame estimated = new UserGame(1L, 620L, 0, Instant.now(), Instant.now());
|
||||
estimated.setFinishedAtEstimate(Instant.parse("2025-06-01T00:00:00Z"));
|
||||
userGameRepository.saveAndFlush(estimated);
|
||||
jdbc.update("UPDATE user_game SET status = 'FINISHED' WHERE user_id = 1 AND appid = 620");
|
||||
PublicStatsService service = new PublicStatsService(statsRepository);
|
||||
ReflectionTestUtils.setField(service, "zone", ZoneId.of("Europe/Berlin"));
|
||||
|
||||
PublicStatsDto dto = service.getPublicStats();
|
||||
|
||||
assertEquals("Steam", dto.gamesPerPlatform().get(0).name());
|
||||
assertEquals(300L, dto.gamesPerPlatform().get(0).minutes());
|
||||
assertEquals("RPG", dto.topGenres().get(0).name());
|
||||
assertEquals(2025, dto.finishedPerYear().get(0).year());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletedUsers_LeaveEveryNumberAndTheTopLists() {
|
||||
steamGameRepository.saveAndFlush(new SteamGame(620L, "Portal 2", null, Instant.now()));
|
||||
userGameRepository.saveAllAndFlush(List.of(new UserGame(3L, 620L, 900, Instant.now(), Instant.now()),
|
||||
new UserGame(3L, 220L, 0, Instant.now(), Instant.now())));
|
||||
finishWithGenreForUser3();
|
||||
jdbc.update("INSERT INTO deleted_user (user_id) VALUES (3)");
|
||||
|
||||
hiddenGames_LeaveEveryNumber();
|
||||
hiddenGames_LeaveTheTopLists();
|
||||
assertLandingListsIgnoreUser3();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -82,9 +171,11 @@ class StatsRepositoryTest extends AbstractPersistenceTest {
|
||||
steamGameRepository.saveAndFlush(new SteamGame(620L, "Portal 2", null, Instant.now()));
|
||||
userGameRepository.saveAllAndFlush(List.of(new UserGame(3L, 620L, 900, Instant.now(), Instant.now()),
|
||||
new UserGame(3L, 220L, 0, Instant.now(), Instant.now())));
|
||||
finishWithGenreForUser3();
|
||||
jdbc.update("INSERT INTO blocked_user (user_id) VALUES (3)");
|
||||
|
||||
hiddenGames_LeaveEveryNumber();
|
||||
hiddenGames_LeaveTheTopLists();
|
||||
assertLandingListsIgnoreUser3();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user