From 12ddf1c25abce82b1c7da83bdade3e6476beb945 Mon Sep 17 00:00:00 2001 From: Alex Lewin Date: Thu, 7 May 2026 17:37:17 +0100 Subject: [PATCH 01/12] Augment gameboards with saved to account info on book detail pages --- .../ac/cam/cl/dtg/isaac/api/PagesFacade.java | 9 +++- .../dtg/isaac/api/managers/GameManager.java | 46 ++++++++++++++++++- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/PagesFacade.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/PagesFacade.java index bba638e41e..0e4d355946 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/PagesFacade.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/PagesFacade.java @@ -1007,7 +1007,14 @@ public final Response getBookDetailPage(@Context final Request request, List additionalGameboardIds = Objects.requireNonNullElse(bookPageDO.getExtensionGameboards(), Collections.emptyList()); List allGameboardIds = Stream.of(gameboardIds, additionalGameboardIds) .flatMap(Collection::stream).collect(Collectors.toList()); - List linkedGameboards = gameManager.getGameboards(allGameboardIds); + + List linkedGameboards; + try { + RegisteredUserDTO registeredUser = userManager.getCurrentRegisteredUser(httpServletRequest); + linkedGameboards = gameManager.getGameboardsWithUserSavedInformation(allGameboardIds, registeredUser); + } catch (final NoUserLoggedInException e) { + linkedGameboards = gameManager.getGameboards(allGameboardIds); + } bookPageDTO.setGameboards(linkedGameboards .stream() diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java index 6f1259925b..4e9710fd8a 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java @@ -338,6 +338,31 @@ public final List getGameboardsWithAttempts(final List gam return gameboardsByIds; } + /** + * Get a list of gameboards by their ids, augmented with whether or not the user has it saved to their boards. + * + * @param gameboardIds + * - to look up. + * @param user + * - the user to augment the gameboard for. + * @return the gameboards or null. + * @throws SegueDatabaseException + * - if there is a problem retrieving the gameboards in the database + */ + public final List getGameboardsWithUserSavedInformation(final List gameboardIds, final RegisteredUserDTO user) + throws SegueDatabaseException { + if (null == gameboardIds || gameboardIds.isEmpty()) { + return new ArrayList<>(); + } + + List gameboardsByIds = this.gameboardPersistenceManager.getGameboardsByIds(gameboardIds); + for (GameboardDTO gameboard : gameboardsByIds) { + augmentGameboardWithUserSavedInformation(gameboard, user); + } + + return gameboardsByIds; + } + /** * Get a gameboard by its id. * @@ -777,6 +802,25 @@ private GameboardDTO augmentGameboardWithQuestionAttemptInformationAndUserInform return gameboardDTO; } + /** + * Augments a gameboard with whether or not the user has it in their boards. + * + * @param gameboardDTO + * - the DTO of the gameboard. + * @param user + * - the user to check whether the board is in their boards list + * @return Augmented Gameboard. + * @throws SegueDatabaseException + * - if there is an error retrieving the content requested. + */ + private GameboardDTO augmentGameboardWithUserSavedInformation(final GameboardDTO gameboardDTO, final AbstractSegueUserDTO user) + throws SegueDatabaseException { + if (user instanceof RegisteredUserDTO registeredUser) { + gameboardDTO.setSavedToCurrentUser(this.isBoardLinkedToUser(registeredUser, gameboardDTO.getId())); + } + + return gameboardDTO; + } /** * Augments the gameboards with question attempt information NOT whether the user has it in their my board page. @@ -786,8 +830,6 @@ private GameboardDTO augmentGameboardWithQuestionAttemptInformationAndUserInform * @param questionAttemptsFromUser * - the users question attempt data. * @return Augmented Gameboard. - * @throws ContentManagerException - * - if there is an error retrieving the content requested. */ private GameboardDTO augmentGameboardWithQuestionAttemptInformation(final GameboardDTO gameboardDTO, final Map>> questionAttemptsFromUser) From f2e277ce6f8dd9f44034090928e794d378e51a41 Mon Sep 17 00:00:00 2001 From: Alex Lewin Date: Fri, 8 May 2026 09:25:35 +0100 Subject: [PATCH 02/12] Augment gameboards with saved to account info on My Assignments --- .../cl/dtg/isaac/api/AssignmentFacade.java | 3 +- .../dtg/isaac/api/managers/GameManager.java | 33 ++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/AssignmentFacade.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/AssignmentFacade.java index d12a9f8e6e..c4756c7992 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/AssignmentFacade.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/AssignmentFacade.java @@ -184,7 +184,8 @@ public Response getAssignments(@Context final HttpServletRequest request) { // Gather all gameboards we need to augment for the assignments in a single query List gameboardIds = assignments.stream().map(AssignmentDTO::getGameboardId).collect(Collectors.toList()); - Map gameboardsMap = this.gameManager.getGameboardsWithAttempts(gameboardIds, currentlyLoggedInUser) + Map gameboardsMap = this.gameManager + .getGameboardsWithAttemptsAndUserSavedInformation(gameboardIds, currentlyLoggedInUser) .stream().collect(Collectors.toMap(GameboardDTO::getId, Function.identity())); // we want to populate gameboard details for the assignment DTO. diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java index 4e9710fd8a..708bf7c77c 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java @@ -339,7 +339,7 @@ public final List getGameboardsWithAttempts(final List gam } /** - * Get a list of gameboards by their ids, augmented with whether or not the user has it saved to their boards. + * Get a list of gameboards by their ids, augmented with whether the user has it saved to their boards. * * @param gameboardIds * - to look up. @@ -363,6 +363,37 @@ public final List getGameboardsWithUserSavedInformation(final List return gameboardsByIds; } + /** + * Get a list of gameboards by their ids, augmented with attempt information and whether the user has it saved to their boards. + * + * @param gameboardIds + * - to look up. + * @param user + * - the user to augment the gameboard for. + * @return the gameboards or null. + * @throws SegueDatabaseException + * - if there is a problem retrieving the gameboards in the database. + * @throws ContentManagerException + * - if there is a problem resolving content + */ + public final List getGameboardsWithAttemptsAndUserSavedInformation(final List gameboardIds, final RegisteredUserDTO user) + throws SegueDatabaseException, ContentManagerException { + if (null == gameboardIds || gameboardIds.isEmpty()) { + return new ArrayList<>(); + } + + List gameboardsByIds = this.gameboardPersistenceManager.getGameboardsByIds(gameboardIds); + List questionPageIds = gameboardsByIds.stream().map(GameboardDTO::getContents).flatMap(Collection::stream).map(GameboardItem::getId).collect(Collectors.toList()); + Map>> userQuestionAttempts = + questionManager.getMatchingLightweightQuestionAttempts(user, questionPageIds); + for (GameboardDTO gameboard : gameboardsByIds) { + augmentGameboardWithQuestionAttemptInformation(gameboard, userQuestionAttempts); + augmentGameboardWithUserSavedInformation(gameboard, user); + } + + return gameboardsByIds; + } + /** * Get a gameboard by its id. * From fc097b724bba1e4d3934e56d96f83de54efaaa1f Mon Sep 17 00:00:00 2001 From: Alex Lewin Date: Fri, 8 May 2026 10:45:47 +0100 Subject: [PATCH 03/12] Use single method to augment with attempt & saved info --- .../cam/cl/dtg/isaac/api/managers/GameManager.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java index 708bf7c77c..361071ac44 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java @@ -364,7 +364,7 @@ public final List getGameboardsWithUserSavedInformation(final List } /** - * Get a list of gameboards by their ids, augmented with attempt information and whether the user has it saved to their boards. + * Get a list of gameboards by their ids, augmented with attempt information AND whether the user has it saved to their boards. * * @param gameboardIds * - to look up. @@ -387,8 +387,7 @@ public final List getGameboardsWithAttemptsAndUserSavedInformation Map>> userQuestionAttempts = questionManager.getMatchingLightweightQuestionAttempts(user, questionPageIds); for (GameboardDTO gameboard : gameboardsByIds) { - augmentGameboardWithQuestionAttemptInformation(gameboard, userQuestionAttempts); - augmentGameboardWithUserSavedInformation(gameboard, user); + augmentGameboardWithQuestionAttemptInformationAndUserInformation(gameboard, userQuestionAttempts, user); } return gameboardsByIds; @@ -436,7 +435,7 @@ public List getLiteGameboards(final Collection gameboardId * @throws ContentManagerException * - if there is an error retrieving the content requested. */ - public final GameboardDTO getGameboard(final String gameboardId, final AbstractSegueUserDTO user, + public final GameboardDTO getGameboard(final String gameboardId, final AbstractSegueUserDTO user, /////////////////////// final Map>> userQuestionAttempts) throws SegueDatabaseException, ContentManagerException { @@ -834,23 +833,20 @@ private GameboardDTO augmentGameboardWithQuestionAttemptInformationAndUserInform } /** - * Augments a gameboard with whether or not the user has it in their boards. + * Augments a gameboard with whether the user has it in their boards. * * @param gameboardDTO * - the DTO of the gameboard. * @param user * - the user to check whether the board is in their boards list - * @return Augmented Gameboard. * @throws SegueDatabaseException * - if there is an error retrieving the content requested. */ - private GameboardDTO augmentGameboardWithUserSavedInformation(final GameboardDTO gameboardDTO, final AbstractSegueUserDTO user) + private void augmentGameboardWithUserSavedInformation(final GameboardDTO gameboardDTO, final AbstractSegueUserDTO user) throws SegueDatabaseException { if (user instanceof RegisteredUserDTO registeredUser) { gameboardDTO.setSavedToCurrentUser(this.isBoardLinkedToUser(registeredUser, gameboardDTO.getId())); } - - return gameboardDTO; } /** From 26c41d3d90aa6458522fe06d54522fc57c7e0384 Mon Sep 17 00:00:00 2001 From: Alex Lewin Date: Fri, 8 May 2026 11:04:00 +0100 Subject: [PATCH 04/12] Revert accidental comment changes --- .../uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java index 361071ac44..c1d5c0f6ba 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java @@ -383,7 +383,8 @@ public final List getGameboardsWithAttemptsAndUserSavedInformation } List gameboardsByIds = this.gameboardPersistenceManager.getGameboardsByIds(gameboardIds); - List questionPageIds = gameboardsByIds.stream().map(GameboardDTO::getContents).flatMap(Collection::stream).map(GameboardItem::getId).collect(Collectors.toList()); + List questionPageIds = gameboardsByIds.stream().map(GameboardDTO::getContents).flatMap(Collection::stream) + .map(GameboardItem::getId).collect(Collectors.toList()); Map>> userQuestionAttempts = questionManager.getMatchingLightweightQuestionAttempts(user, questionPageIds); for (GameboardDTO gameboard : gameboardsByIds) { @@ -435,7 +436,7 @@ public List getLiteGameboards(final Collection gameboardId * @throws ContentManagerException * - if there is an error retrieving the content requested. */ - public final GameboardDTO getGameboard(final String gameboardId, final AbstractSegueUserDTO user, /////////////////////// + public final GameboardDTO getGameboard(final String gameboardId, final AbstractSegueUserDTO user, final Map>> userQuestionAttempts) throws SegueDatabaseException, ContentManagerException { @@ -857,6 +858,8 @@ private void augmentGameboardWithUserSavedInformation(final GameboardDTO gameboa * @param questionAttemptsFromUser * - the users question attempt data. * @return Augmented Gameboard. + * @throws ContentManagerException + * - if there is an error retrieving the content requested. */ private GameboardDTO augmentGameboardWithQuestionAttemptInformation(final GameboardDTO gameboardDTO, final Map>> questionAttemptsFromUser) From 980e93686750fcabba46c44fc365b9bdb90f5956 Mon Sep 17 00:00:00 2001 From: Alex Lewin Date: Wed, 13 May 2026 11:55:45 +0100 Subject: [PATCH 05/12] Replace `isBoardLinkedToUser` db method with bulk methods --- .../dtg/isaac/api/managers/GameManager.java | 6 +- .../dao/GameboardPersistenceManager.java | 74 ++++++++++++++----- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java index c1d5c0f6ba..698f8ac4c5 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java @@ -1029,8 +1029,10 @@ private static List depthFirstDOQuestionSearch(final Content c, final L */ private boolean isBoardLinkedToUser(final RegisteredUserDTO user, final String gameboardId) throws SegueDatabaseException { - return this.gameboardPersistenceManager.isBoardLinkedToUser(user.getId(), gameboardId); - } + Set linkedIds = this.gameboardPersistenceManager + .getGameboardIdsLinkedToUser(user.getId(), Collections.singleton(gameboardId)); + return linkedIds.contains(gameboardId); + } /** * Store a gameboard in a public location. diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/dao/GameboardPersistenceManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/dao/GameboardPersistenceManager.java index 95da801764..33fed3a785 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/dao/GameboardPersistenceManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/dao/GameboardPersistenceManager.java @@ -23,6 +23,7 @@ import com.google.common.cache.CacheBuilder; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.google.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -77,6 +78,7 @@ public class GameboardPersistenceManager { private static final Logger log = LoggerFactory.getLogger(GameboardPersistenceManager.class); private static final Long GAMEBOARD_TTL_MINUTES = 30L; private static final int GAMEBOARD_ITEM_MAP_BATCH_SIZE = 1000; + private static final int MAX_GAMEBOARD_IDS_TO_MATCH = 50; private final PostgresSqlDb database; private final Cache gameboardNonPersistentStorage; @@ -260,34 +262,70 @@ public boolean isPermanentlyStored(final String gameboardIdToTest) throws SegueD } /** - * Determines whether a given game board is already in a users my boards list. Only boards in persistent storage - * should be linked to a user. - * - * @param userId - * to check - * @param gameboardId - * to look up - * @return true if it is false if not - * @throws SegueDatabaseException - * if there is a database error + * Determines which of a given collection of gameboard IDs are in a user's saved gameboards. + * + * IMPORTANT: If too many gameboard IDs are provided, this instead returns ALL of the user's saved board IDs. + * + * @param userId the user to check saved gameboards for + * @param gameboardIds the list of gameboard IDs to check + * @return the subset of the provided gameboard IDs that the user has saved OR all of a user's saved gameboard IDs + * if the provided list of gameboard IDs is too long. + * @throws SegueDatabaseException if there is a database error */ - public boolean isBoardLinkedToUser(final Long userId, final String gameboardId) throws SegueDatabaseException { - if (userId == null || gameboardId == null) { - return false; + public Set getGameboardIdsLinkedToUser(final Long userId, final Collection gameboardIds) throws SegueDatabaseException { + if (gameboardIds.size() > MAX_GAMEBOARD_IDS_TO_MATCH) { + log.debug("Attempting to match too many ({}) gameboard IDs; returning all saved boards for the user instead!", + gameboardIds.size()); + return getGameboardIdsLinkedToUser(userId); } - String query = "SELECT COUNT(*) AS TOTAL FROM user_gameboards WHERE user_id = ? AND gameboard_id = ?;"; + Set linkedGameboardIds = Sets.newHashSet(); + + String query = "SELECT gameboard_id FROM user_gameboards WHERE user_id = ? AND gameboard_id = ANY(?);"; try (Connection conn = database.getDatabaseConnection(); PreparedStatement pst = conn.prepareStatement(query); ) { pst.setLong(1, userId); - pst.setObject(2, gameboardId); + + Array gameboardIdArray = conn.createArrayOf("TEXT", gameboardIds.toArray()); + pst.setArray(2, gameboardIdArray); try (ResultSet results = pst.executeQuery()) { - results.next(); - return results.getInt("TOTAL") == 1; + while (results.next()) { + linkedGameboardIds.add(results.getString("gameboard_id")); + } + return linkedGameboardIds; + } finally { + gameboardIdArray.free(); } - } catch (SQLException e) { + } catch (final SQLException e) { + throw new SegueDatabaseException("Postgres exception", e); + } + } + + /** + * Gets the gameboard IDs that a user has in their saved boards. + * + * @param userId the user to check saved gameboards for + * @return a set of the user's saved gameboard IDs + * @throws SegueDatabaseException if there is a database error + */ + public Set getGameboardIdsLinkedToUser(final Long userId) throws SegueDatabaseException { + Set linkedGameboardIds = Sets.newHashSet(); + + String query = "SELECT gameboard_id FROM user_gameboards WHERE user_id = ?;"; + try (Connection conn = database.getDatabaseConnection(); + PreparedStatement pst = conn.prepareStatement(query); + ) { + pst.setLong(1, userId); + + try (ResultSet results = pst.executeQuery()) { + while (results.next()) { + linkedGameboardIds.add(results.getString("gameboard_id")); + } + return linkedGameboardIds; + } + } catch (final SQLException e) { throw new SegueDatabaseException("Postgres exception", e); } } From 2b3b4b9b3ffe95f71f7682edda25d483facd0cd4 Mon Sep 17 00:00:00 2001 From: Alex Lewin Date: Wed, 13 May 2026 12:25:10 +0100 Subject: [PATCH 06/12] Augment gameboards with attempt & saved info separately --- .../dtg/isaac/api/managers/GameManager.java | 50 +++++++------------ 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java index 698f8ac4c5..2f784cc9c3 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java @@ -388,7 +388,15 @@ public final List getGameboardsWithAttemptsAndUserSavedInformation Map>> userQuestionAttempts = questionManager.getMatchingLightweightQuestionAttempts(user, questionPageIds); for (GameboardDTO gameboard : gameboardsByIds) { - augmentGameboardWithQuestionAttemptInformationAndUserInformation(gameboard, userQuestionAttempts, user); + augmentGameboardWithQuestionAttemptInformation(gameboard, userQuestionAttempts); + } + + Set savedBoardIds = this.gameboardPersistenceManager.getGameboardIdsLinkedToUser(user.getId(), gameboardIds); + // getGameboardIdsLinkedToUser may return IDs that weren't in the original list, so take the intersection: + savedBoardIds.retainAll(gameboardIds); + + for (GameboardDTO gameboard : gameboardsByIds) { + gameboard.setSavedToCurrentUser(savedBoardIds.contains(gameboard.getId())); } return gameboardsByIds; @@ -440,10 +448,17 @@ public final GameboardDTO getGameboard(final String gameboardId, final AbstractS final Map>> userQuestionAttempts) throws SegueDatabaseException, ContentManagerException { + GameboardDTO gameboard = this.gameboardPersistenceManager.getGameboardById(gameboardId); + gameboard = augmentGameboardWithQuestionAttemptInformation(gameboard, userQuestionAttempts); // we need to augment the DTO with whether this gameboard is in a users my boards list. - return augmentGameboardWithQuestionAttemptInformationAndUserInformation( - this.gameboardPersistenceManager.getGameboardById(gameboardId), userQuestionAttempts, user); + if (user instanceof RegisteredUserDTO registeredUser) { + Set savedBoardIDs = this.gameboardPersistenceManager + .getGameboardIdsLinkedToUser(registeredUser.getId(), Collections.singletonList(gameboardId)); + gameboard.setSavedToCurrentUser(savedBoardIDs.contains(gameboardId)); + } + + return gameboard; } /** @@ -804,35 +819,6 @@ public static List getAllMarkableDOQuestionPartsDFSOrder(final Content return filterDOQuestionParts(dfs); } - /** - * Augments the gameboards with question attempt information AND whether or not the user has it in their boards. - * - * @param gameboardDTO - * - the DTO of the gameboard. - * @param questionAttemptsFromUser - * - the users question attempt data. - * @param user - * - the user to check whether the board is in their boards list - * @return Augmented Gameboard. - * @throws SegueDatabaseException - * - if there is an error retrieving the content requested. - * @throws ContentManagerException - * - if there is an error retrieving the content requested. - */ - private GameboardDTO augmentGameboardWithQuestionAttemptInformationAndUserInformation(final GameboardDTO gameboardDTO, - final Map>> questionAttemptsFromUser, - final AbstractSegueUserDTO user) - throws SegueDatabaseException, ContentManagerException { - if (user instanceof RegisteredUserDTO registeredUser) { - gameboardDTO - .setSavedToCurrentUser(this.isBoardLinkedToUser(registeredUser, gameboardDTO.getId())); - } - - this.augmentGameboardWithQuestionAttemptInformation(gameboardDTO, questionAttemptsFromUser); - - return gameboardDTO; - } - /** * Augments a gameboard with whether the user has it in their boards. * From 6f817b8873da64321fb14c4092d1214542a0ebdb Mon Sep 17 00:00:00 2001 From: Alex Lewin Date: Wed, 13 May 2026 16:49:46 +0100 Subject: [PATCH 07/12] Simplify augmentation logic --- .../uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java index 2f784cc9c3..2ad3f544e4 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java @@ -385,17 +385,13 @@ public final List getGameboardsWithAttemptsAndUserSavedInformation List gameboardsByIds = this.gameboardPersistenceManager.getGameboardsByIds(gameboardIds); List questionPageIds = gameboardsByIds.stream().map(GameboardDTO::getContents).flatMap(Collection::stream) .map(GameboardItem::getId).collect(Collectors.toList()); + Map>> userQuestionAttempts = questionManager.getMatchingLightweightQuestionAttempts(user, questionPageIds); - for (GameboardDTO gameboard : gameboardsByIds) { - augmentGameboardWithQuestionAttemptInformation(gameboard, userQuestionAttempts); - } - Set savedBoardIds = this.gameboardPersistenceManager.getGameboardIdsLinkedToUser(user.getId(), gameboardIds); - // getGameboardIdsLinkedToUser may return IDs that weren't in the original list, so take the intersection: - savedBoardIds.retainAll(gameboardIds); for (GameboardDTO gameboard : gameboardsByIds) { + augmentGameboardWithQuestionAttemptInformation(gameboard, userQuestionAttempts); gameboard.setSavedToCurrentUser(savedBoardIds.contains(gameboard.getId())); } From 233a4018b6b596592fa46a88fdd974d9c07709b5 Mon Sep 17 00:00:00 2001 From: Alex Lewin Date: Wed, 13 May 2026 16:50:56 +0100 Subject: [PATCH 08/12] Get saved gameboard info in bulk ...and remove now-unused method --- .../java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java index 2ad3f544e4..04ffe807e1 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java @@ -356,8 +356,10 @@ public final List getGameboardsWithUserSavedInformation(final List } List gameboardsByIds = this.gameboardPersistenceManager.getGameboardsByIds(gameboardIds); + Set savedBoardIds = this.gameboardPersistenceManager.getGameboardIdsLinkedToUser(user.getId(), gameboardIds); + for (GameboardDTO gameboard : gameboardsByIds) { - augmentGameboardWithUserSavedInformation(gameboard, user); + gameboard.setSavedToCurrentUser(savedBoardIds.contains(gameboard.getId())); } return gameboardsByIds; From 35c0b1dae04dfb0623bc6d6e645a206ebc5bd457 Mon Sep 17 00:00:00 2001 From: Alex Lewin Date: Wed, 13 May 2026 16:51:25 +0100 Subject: [PATCH 09/12] Remove unused method --- .../dtg/isaac/api/managers/GameManager.java | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java index 04ffe807e1..9ba6d019bc 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java @@ -305,39 +305,6 @@ public final List getGameboards(final List gameboardIds, return gameboardsByIds; } - /** - * Get a list of gameboards by their ids, augmented with attempt information. - * - * Note: These gameboards WILL be augmented with user attempt information, but not whether the gameboard is saved - * to the user's boards. - * - * @param gameboardIds - * - to look up. - * @param user - * - the user to augment the gameboard for. - * @return the gameboards or null. - * @throws SegueDatabaseException - * - if there is a problem retrieving the gameboards in the database. - * @throws ContentManagerException - * - if there is a problem resolving content - */ - public final List getGameboardsWithAttempts(final List gameboardIds, final RegisteredUserDTO user) - throws SegueDatabaseException, ContentManagerException { - if (null == gameboardIds || gameboardIds.isEmpty()) { - return new ArrayList<>(); - } - - List gameboardsByIds = this.gameboardPersistenceManager.getGameboardsByIds(gameboardIds); - List questionPageIds = gameboardsByIds.stream().map(GameboardDTO::getContents).flatMap(Collection::stream).map(GameboardItem::getId).collect(Collectors.toList()); - Map>> userQuestionAttempts = - questionManager.getMatchingLightweightQuestionAttempts(user, questionPageIds); - for (GameboardDTO gb : gameboardsByIds) { - augmentGameboardWithQuestionAttemptInformation(gb, userQuestionAttempts); - } - - return gameboardsByIds; - } - /** * Get a list of gameboards by their ids, augmented with whether the user has it saved to their boards. * From df392e30598ffc4faf621c5e7bcfc1cf31f2d7a8 Mon Sep 17 00:00:00 2001 From: Alex Lewin Date: Wed, 13 May 2026 16:52:58 +0100 Subject: [PATCH 10/12] Actually remove unused method as claimed in https://github.com/isaacphysics/isaac-api/commit/233a4018b6b596592fa46a88fdd974d9c07709b5 --- .../cl/dtg/isaac/api/managers/GameManager.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java index 9ba6d019bc..794e828a15 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java @@ -784,23 +784,6 @@ public static List getAllMarkableDOQuestionPartsDFSOrder(final Content return filterDOQuestionParts(dfs); } - /** - * Augments a gameboard with whether the user has it in their boards. - * - * @param gameboardDTO - * - the DTO of the gameboard. - * @param user - * - the user to check whether the board is in their boards list - * @throws SegueDatabaseException - * - if there is an error retrieving the content requested. - */ - private void augmentGameboardWithUserSavedInformation(final GameboardDTO gameboardDTO, final AbstractSegueUserDTO user) - throws SegueDatabaseException { - if (user instanceof RegisteredUserDTO registeredUser) { - gameboardDTO.setSavedToCurrentUser(this.isBoardLinkedToUser(registeredUser, gameboardDTO.getId())); - } - } - /** * Augments the gameboards with question attempt information NOT whether the user has it in their my board page. * From 842185cd68aa670783269a0594891f94ea3c2f9e Mon Sep 17 00:00:00 2001 From: James Sharkey Date: Wed, 13 May 2026 17:25:48 +0100 Subject: [PATCH 11/12] Avoid creating new empty lists --- .../uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java index 794e828a15..373209efdd 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java @@ -60,7 +60,6 @@ import jakarta.annotation.Nullable; import jakarta.validation.constraints.NotNull; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -294,7 +293,7 @@ public final List getGameboards(final List gameboardIds, final Map>> userQuestionAttempts) throws SegueDatabaseException, ContentManagerException { if (null == gameboardIds || gameboardIds.isEmpty()) { - return new ArrayList<>(); + return Collections.emptyList(); } List gameboardsByIds = this.gameboardPersistenceManager.getGameboardsByIds(gameboardIds); @@ -319,7 +318,7 @@ public final List getGameboards(final List gameboardIds, public final List getGameboardsWithUserSavedInformation(final List gameboardIds, final RegisteredUserDTO user) throws SegueDatabaseException { if (null == gameboardIds || gameboardIds.isEmpty()) { - return new ArrayList<>(); + return Collections.emptyList(); } List gameboardsByIds = this.gameboardPersistenceManager.getGameboardsByIds(gameboardIds); @@ -348,7 +347,7 @@ public final List getGameboardsWithUserSavedInformation(final List public final List getGameboardsWithAttemptsAndUserSavedInformation(final List gameboardIds, final RegisteredUserDTO user) throws SegueDatabaseException, ContentManagerException { if (null == gameboardIds || gameboardIds.isEmpty()) { - return new ArrayList<>(); + return Collections.emptyList(); } List gameboardsByIds = this.gameboardPersistenceManager.getGameboardsByIds(gameboardIds); From 325d08d279019ffaf310e14debf87aae04ef5136 Mon Sep 17 00:00:00 2001 From: Alex Lewin Date: Wed, 13 May 2026 17:34:23 +0100 Subject: [PATCH 12/12] Use `isBoardLinkedToUser` helper --- .../java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java index 373209efdd..a87c12a6e1 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java @@ -417,9 +417,7 @@ public final GameboardDTO getGameboard(final String gameboardId, final AbstractS // we need to augment the DTO with whether this gameboard is in a users my boards list. if (user instanceof RegisteredUserDTO registeredUser) { - Set savedBoardIDs = this.gameboardPersistenceManager - .getGameboardIdsLinkedToUser(registeredUser.getId(), Collections.singletonList(gameboardId)); - gameboard.setSavedToCurrentUser(savedBoardIDs.contains(gameboardId)); + gameboard.setSavedToCurrentUser(isBoardLinkedToUser(registeredUser, gameboardId)); } return gameboard;