From 6215bb0dec203638b9f5efbdbc396e2f6d487521 Mon Sep 17 00:00:00 2001 From: lobo Date: Sun, 11 Oct 2026 12:11:47 +0200 Subject: [PATCH] fix(library): count trend never played by set total Nightly snapshot and live trend point use the set total, as stats do. Newest point now equals stats "never started". Old rows keep old value. Refs #43 --- readme.md | 3 ++- .../repo/BacklogSnapshotRepository.java | 4 +++- .../repo/BacklogSnapshotRepositoryTest.java | 24 +++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 118d789..766a3a0 100644 --- a/readme.md +++ b/readme.md @@ -124,7 +124,8 @@ flowchart LR moods, the total playtime and moon trips, the drill-down lists) uses the set total, the view `user_game_entry_playtime` (V45), the same as the library card, sort and public profile. So do "played", "never started" and "under one hour": a set counts once, judged by its set total (also the dashboard "never played" and the - oldest unplayed game). Only the nightly trend snapshot `never_played` still uses the counted edition's own minutes. + oldest unplayed game) and the trend's never played (`backlog_snapshot`); rows written before #43 keep the old value, + they cannot be rebuilt. Platforms stay per edition: a Steam + PlayStation set counts on both platforms. - **Drill-down**: every countable number opens the games behind it. A `StatKey` (library), `CommunityKey` (landing page) or a Pile Points track/bonus row names a number; its SQL "game set" is the same text the number counts, so the diff --git a/src/main/java/core_service/modules/library/repo/BacklogSnapshotRepository.java b/src/main/java/core_service/modules/library/repo/BacklogSnapshotRepository.java index 1770c5d..92b99a3 100644 --- a/src/main/java/core_service/modules/library/repo/BacklogSnapshotRepository.java +++ b/src/main/java/core_service/modules/library/repo/BacklogSnapshotRepository.java @@ -12,6 +12,7 @@ import java.util.List; /** * The daily pile size per user, table backlog_snapshot. Counts listed games only, like {@link UserGameRepository#LISTED}, and a linked set once (V28). + * Never played is judged by the set total (V45), like {@link UserGameRepository#countListedNeverPlayed}. */ @Repository @RequiredArgsConstructor @@ -19,11 +20,12 @@ public class BacklogSnapshotRepository { private static final String PILE_COLUMNS = """ count(*) filter (where ug.status = 'BACKLOG' and cg.appid is null) as backlog_games, - count(*) filter (where ug.playtime_minutes = 0) as never_played, + count(*) filter (where ep.playtime_minutes = 0) as never_played, coalesce(sum(t.normally_minutes) filter (where ug.status = 'BACKLOG' and cg.appid is null), 0) as backlog_minutes, count(*) as listed_games from counted_user_game ug left join game_time_to_beat t on t.appid = ug.appid left join coming_game cg on cg.appid = ug.appid + left join user_game_entry_playtime ep on ep.user_id = ug.user_id and ep.appid = ug.appid """; private final NamedParameterJdbcTemplate jdbc; diff --git a/src/test/java/core_service/modules/library/repo/BacklogSnapshotRepositoryTest.java b/src/test/java/core_service/modules/library/repo/BacklogSnapshotRepositoryTest.java index 3ae0229..552f6a8 100644 --- a/src/test/java/core_service/modules/library/repo/BacklogSnapshotRepositoryTest.java +++ b/src/test/java/core_service/modules/library/repo/BacklogSnapshotRepositoryTest.java @@ -86,6 +86,30 @@ class BacklogSnapshotRepositoryTest extends AbstractPersistenceTest { assertThat(repository.findSince(1L, DAY)).containsExactly(new UserStats.PileDay(DAY, 1, 1, 600)); } + private void psnPlay(long appid, int minutes) { + jdbc.update("insert into user_psn_title (user_id, psn_key, state, appid, platform, playtime_minutes) " + + "values (1, ?, 'STEAM_OWNED', ?, 'PS5', ?)", "c" + appid, appid, minutes); + } + + @Test + void writeAll_ASetPlayedOnlyOnPlayStationIsNotNeverPlayed() { + userGameRepository.saveAllAndFlush(List.of(game(1L, 220L, 0, GameStatus.BACKLOG), game(1L, 620L, 0, GameStatus.BACKLOG))); + psnPlay(220L, 120); + + repository.writeAll(DAY); + + assertThat(repository.findSince(1L, DAY)).extracting(UserStats.PileDay::neverPlayed).containsExactly(1L); + assertThat(userGameRepository.countListedNeverPlayed(1L)).isEqualTo(1); + } + + @Test + void live_UsesTheSetTotal() { + userGameRepository.saveAllAndFlush(List.of(game(1L, 220L, 0, GameStatus.BACKLOG), game(1L, 620L, 0, GameStatus.BACKLOG))); + psnPlay(220L, 120); + + assertThat(repository.live(1L, DAY).neverPlayed()).isEqualTo(1).isEqualTo(userGameRepository.countListedNeverPlayed(1L)); + } + @Test void writeAll_SecondRunOnTheSameDay_ReplacesTheRow() { UserGame game = game(1L, 220L, 0, GameStatus.BACKLOG);