From 2c4be0f306d6fe703f176225a5af14032bb7ea00 Mon Sep 17 00:00:00 2001 From: Sol Dubock <94075844+sjd210@users.noreply.github.com> Date: Tue, 24 Feb 2026 10:23:20 +0000 Subject: [PATCH 1/6] Count correct question parts per question --- .../segue/api/managers/StatisticsManager.java | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java b/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java index 0ddb43f92a..343cc11919 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java @@ -198,6 +198,7 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us Map questionsCorrectByTypeStats = Maps.newHashMap(); List incompleteQuestionPages = Lists.newArrayList(); Queue mostRecentlyAttemptedQuestionPages = new CircularFifoQueue<>(PROGRESS_MAX_RECENT_QUESTIONS); + List mostRecentlyAttemptedQuestionsList = Lists.newArrayList(); LocalDate now = LocalDate.now(); LocalDate endOfAugustThisYear = LocalDate.of(now.getYear(), Month.AUGUST, 31); @@ -218,14 +219,17 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us } IsaacQuestionPageDTO questionPageDTO = (IsaacQuestionPageDTO) contentDTO; - mostRecentlyAttemptedQuestionPages.add(questionPageDTO); // Assumes questionAttemptsByUser is sorted! attemptedQuestions++; boolean questionIsCorrect = true; // Are all Parts of the Question correct? LocalDate mostRecentCorrectQuestionPart = null; LocalDate mostRecentAttemptAtQuestion = null; + int questionPartsCorrect = 0; + int questionPartsIncorrect = 0; + int questionPartsTotal = 0; // Loop through each Part of the Question: for (QuestionDTO questionPart : GameManager.getAllMarkableQuestionPartsDFSOrder(questionPageDTO)) { + questionPartsTotal++; boolean questionPartIsCorrect = false; // Is this Part of the Question correct? // Has the user attempted this part of the question at all? if (question.getValue().containsKey(questionPart.getId())) { @@ -241,6 +245,7 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us mostRecentAttemptAtThisQuestionPart = dateAttempted; } if (validationResponse.isCorrect() != null && validationResponse.isCorrect()) { + questionPartsCorrect++; correctQuestionParts++; if (dateAttempted.isAfter(lastDayOfPreviousAcademicYear)) { correctQuestionPartsThisAcademicYear++; @@ -251,6 +256,7 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us questionPartIsCorrect = true; break; // early so that later attempts are ignored } + } // Type Stats - Count the attempt at the Question Part: @@ -284,6 +290,20 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us // Correctness of whole Question: is the Question correct so far, and is this Question Part also correct? questionIsCorrect = questionIsCorrect && questionPartIsCorrect; } + ContentSummaryDTO contentSummaryDTO = contentSummarizerService.extractContentSummary(questionPageDTO); + + + mostRecentlyAttemptedQuestionPages.add(questionPageDTO); // Assumes questionAttemptsByUser is sorted! + + + mostRecentlyAttemptedQuestionsList.add(contentSummaryDTO); + + List mostRecentlyAttemptedQuestionsList = mostRecentlyAttemptedQuestionPages + .stream().map(page -> { + ContentSummaryDTO dto = contentSummarizerService.extractContentSummary(page); + // dto.setState(page.getState()); // derive from source + return dto; + }).collect(Collectors.toList()); // Tag Stats - Loop through the Question's tags: for (String tag : questionPageDTO.getTags()) { @@ -364,7 +384,11 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us // Create the recent and unanswered question lists: List mostRecentlyAttemptedQuestionsList = mostRecentlyAttemptedQuestionPages - .stream().map(contentSummarizerService::extractContentSummary).collect(Collectors.toList()); + .stream().map(page -> { + ContentSummaryDTO dto = contentSummarizerService.extractContentSummary(page); + // dto.setState(page.getState()); // derive from source + return dto; + }).collect(Collectors.toList()); Collections.reverse(mostRecentlyAttemptedQuestionsList); // We want most-recent first order and streams cannot reverse. List questionsNotCompleteList = incompleteQuestionPages.stream() .sorted(Comparator.comparing(SeguePageDTO::getDeprecated, Comparator.nullsFirst(Comparator.naturalOrder()))) From 12be6d675b4a50795a608e91a630809505d03323 Mon Sep 17 00:00:00 2001 From: Sol Dubock <94075844+sjd210@users.noreply.github.com> Date: Tue, 24 Feb 2026 10:38:47 +0000 Subject: [PATCH 2/6] Replace ContentDTO list with SummaryDTO list --- .../segue/api/managers/StatisticsManager.java | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java b/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java index 343cc11919..312932ea3d 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java @@ -22,6 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.ac.cam.cl.dtg.isaac.api.managers.GameManager; +import uk.ac.cam.cl.dtg.isaac.api.managers.UserAttemptManager; import uk.ac.cam.cl.dtg.isaac.api.services.ContentSummarizerService; import uk.ac.cam.cl.dtg.isaac.dos.AudienceContext; import uk.ac.cam.cl.dtg.isaac.dos.Difficulty; @@ -197,7 +198,6 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us Map questionAttemptsByTypeStats = Maps.newHashMap(); Map questionsCorrectByTypeStats = Maps.newHashMap(); List incompleteQuestionPages = Lists.newArrayList(); - Queue mostRecentlyAttemptedQuestionPages = new CircularFifoQueue<>(PROGRESS_MAX_RECENT_QUESTIONS); List mostRecentlyAttemptedQuestionsList = Lists.newArrayList(); LocalDate now = LocalDate.now(); @@ -291,20 +291,10 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us questionIsCorrect = questionIsCorrect && questionPartIsCorrect; } ContentSummaryDTO contentSummaryDTO = contentSummarizerService.extractContentSummary(questionPageDTO); - - - mostRecentlyAttemptedQuestionPages.add(questionPageDTO); // Assumes questionAttemptsByUser is sorted! - + contentSummaryDTO.setState(UserAttemptManager.getCompletionState(questionPartsTotal, questionPartsCorrect, questionPartsIncorrect)); mostRecentlyAttemptedQuestionsList.add(contentSummaryDTO); - List mostRecentlyAttemptedQuestionsList = mostRecentlyAttemptedQuestionPages - .stream().map(page -> { - ContentSummaryDTO dto = contentSummarizerService.extractContentSummary(page); - // dto.setState(page.getState()); // derive from source - return dto; - }).collect(Collectors.toList()); - // Tag Stats - Loop through the Question's tags: for (String tag : questionPageDTO.getTags()) { // Count the attempt at the Question: @@ -382,13 +372,6 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us // Collate all the information into the JSON response as a Map: Map questionInfo = Maps.newHashMap(); - // Create the recent and unanswered question lists: - List mostRecentlyAttemptedQuestionsList = mostRecentlyAttemptedQuestionPages - .stream().map(page -> { - ContentSummaryDTO dto = contentSummarizerService.extractContentSummary(page); - // dto.setState(page.getState()); // derive from source - return dto; - }).collect(Collectors.toList()); Collections.reverse(mostRecentlyAttemptedQuestionsList); // We want most-recent first order and streams cannot reverse. List questionsNotCompleteList = incompleteQuestionPages.stream() .sorted(Comparator.comparing(SeguePageDTO::getDeprecated, Comparator.nullsFirst(Comparator.naturalOrder()))) From 39e92fd66099c411c7aa2e3e4ba101c86d0f44e7 Mon Sep 17 00:00:00 2001 From: Sol Dubock <94075844+sjd210@users.noreply.github.com> Date: Tue, 24 Feb 2026 10:58:05 +0000 Subject: [PATCH 3/6] Replace List with Queue to limit question count --- .../ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java b/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java index 312932ea3d..8778f73292 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java @@ -198,7 +198,7 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us Map questionAttemptsByTypeStats = Maps.newHashMap(); Map questionsCorrectByTypeStats = Maps.newHashMap(); List incompleteQuestionPages = Lists.newArrayList(); - List mostRecentlyAttemptedQuestionsList = Lists.newArrayList(); + Queue mostRecentlyAttemptedQuestionsQueue = new CircularFifoQueue<>(PROGRESS_MAX_RECENT_QUESTIONS); LocalDate now = LocalDate.now(); LocalDate endOfAugustThisYear = LocalDate.of(now.getYear(), Month.AUGUST, 31); @@ -293,7 +293,7 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us ContentSummaryDTO contentSummaryDTO = contentSummarizerService.extractContentSummary(questionPageDTO); contentSummaryDTO.setState(UserAttemptManager.getCompletionState(questionPartsTotal, questionPartsCorrect, questionPartsIncorrect)); - mostRecentlyAttemptedQuestionsList.add(contentSummaryDTO); + mostRecentlyAttemptedQuestionsQueue.add(contentSummaryDTO); // Tag Stats - Loop through the Question's tags: for (String tag : questionPageDTO.getTags()) { @@ -371,7 +371,7 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us // Collate all the information into the JSON response as a Map: Map questionInfo = Maps.newHashMap(); - + List mostRecentlyAttemptedQuestionsList = new ArrayList<>(mostRecentlyAttemptedQuestionsQueue); Collections.reverse(mostRecentlyAttemptedQuestionsList); // We want most-recent first order and streams cannot reverse. List questionsNotCompleteList = incompleteQuestionPages.stream() .sorted(Comparator.comparing(SeguePageDTO::getDeprecated, Comparator.nullsFirst(Comparator.naturalOrder()))) From 06de127db128a3743de2ca8f0db681934fcb913e Mon Sep 17 00:00:00 2001 From: Sol Dubock <94075844+sjd210@users.noreply.github.com> Date: Tue, 24 Feb 2026 11:26:56 +0000 Subject: [PATCH 4/6] Add completion state to incomplete question pages --- .../segue/api/managers/StatisticsManager.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java b/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java index 8778f73292..b241aa0564 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java @@ -197,7 +197,7 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us Map> questionsCorrectByStageAndDifficultyStats = Maps.newHashMap(); Map questionAttemptsByTypeStats = Maps.newHashMap(); Map questionsCorrectByTypeStats = Maps.newHashMap(); - List incompleteQuestionPages = Lists.newArrayList(); + List incompleteQuestionPages = Lists.newArrayList(); Queue mostRecentlyAttemptedQuestionsQueue = new CircularFifoQueue<>(PROGRESS_MAX_RECENT_QUESTIONS); LocalDate now = LocalDate.now(); @@ -245,7 +245,6 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us mostRecentAttemptAtThisQuestionPart = dateAttempted; } if (validationResponse.isCorrect() != null && validationResponse.isCorrect()) { - questionPartsCorrect++; correctQuestionParts++; if (dateAttempted.isAfter(lastDayOfPreviousAcademicYear)) { correctQuestionPartsThisAcademicYear++; @@ -284,15 +283,20 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us } else { questionsCorrectByTypeStats.put(questionPartType, 1); } + + questionPartsCorrect++; + } else { + questionPartsIncorrect++; } } // Correctness of whole Question: is the Question correct so far, and is this Question Part also correct? questionIsCorrect = questionIsCorrect && questionPartIsCorrect; } + + // Convert QuestionPageDTO to ContentSummaryDTO and calculate completion state ContentSummaryDTO contentSummaryDTO = contentSummarizerService.extractContentSummary(questionPageDTO); contentSummaryDTO.setState(UserAttemptManager.getCompletionState(questionPartsTotal, questionPartsCorrect, questionPartsIncorrect)); - mostRecentlyAttemptedQuestionsQueue.add(contentSummaryDTO); // Tag Stats - Loop through the Question's tags: @@ -365,7 +369,7 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us correctQuestionsThisAcademicYear++; } } else { - incompleteQuestionPages.add(questionPageDTO); + incompleteQuestionPages.add(contentSummaryDTO); } } @@ -374,9 +378,8 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us List mostRecentlyAttemptedQuestionsList = new ArrayList<>(mostRecentlyAttemptedQuestionsQueue); Collections.reverse(mostRecentlyAttemptedQuestionsList); // We want most-recent first order and streams cannot reverse. List questionsNotCompleteList = incompleteQuestionPages.stream() - .sorted(Comparator.comparing(SeguePageDTO::getDeprecated, Comparator.nullsFirst(Comparator.naturalOrder()))) - .limit(PROGRESS_MAX_RECENT_QUESTIONS) - .map(contentSummarizerService::extractContentSummary).collect(Collectors.toList()); + .sorted(Comparator.comparing(ContentSummaryDTO::getDeprecated, Comparator.nullsFirst(Comparator.naturalOrder()))) + .limit(PROGRESS_MAX_RECENT_QUESTIONS).collect(Collectors.toList()); questionInfo.put("totalQuestionsAttempted", attemptedQuestions); questionInfo.put("totalQuestionsCorrect", correctQuestions); From 6c5bf8a2e0116f1feb15e283ec1c48e897037c95 Mon Sep 17 00:00:00 2001 From: Sol Dubock <94075844+sjd210@users.noreply.github.com> Date: Tue, 24 Feb 2026 17:56:41 +0000 Subject: [PATCH 5/6] Map to ContentSummaryDTO outside the loop --- .../segue/api/managers/StatisticsManager.java | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java b/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java index b241aa0564..0e00f1c280 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java @@ -35,7 +35,6 @@ import uk.ac.cam.cl.dtg.isaac.dto.content.ContentDTO; import uk.ac.cam.cl.dtg.isaac.dto.content.ContentSummaryDTO; import uk.ac.cam.cl.dtg.isaac.dto.content.QuestionDTO; -import uk.ac.cam.cl.dtg.isaac.dto.content.SeguePageDTO; import uk.ac.cam.cl.dtg.isaac.dto.users.RegisteredUserDTO; import uk.ac.cam.cl.dtg.segue.dao.ILogManager; import uk.ac.cam.cl.dtg.segue.dao.SegueDatabaseException; @@ -49,13 +48,14 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Queue; import java.util.stream.Collectors; +import java.util.stream.Stream; import static com.google.common.collect.Maps.immutableEntry; import static uk.ac.cam.cl.dtg.isaac.api.Constants.*; @@ -198,7 +198,9 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us Map questionAttemptsByTypeStats = Maps.newHashMap(); Map questionsCorrectByTypeStats = Maps.newHashMap(); List incompleteQuestionPages = Lists.newArrayList(); - Queue mostRecentlyAttemptedQuestionsQueue = new CircularFifoQueue<>(PROGRESS_MAX_RECENT_QUESTIONS); + List incompleteDeprecatedQuestionPages = Lists.newArrayList(); + Queue mostRecentlyAttemptedQuestionsQueue = new CircularFifoQueue<>(PROGRESS_MAX_RECENT_QUESTIONS); + Queue mostRecentlyAttemptedQuestionsState = new CircularFifoQueue<>(PROGRESS_MAX_RECENT_QUESTIONS); LocalDate now = LocalDate.now(); LocalDate endOfAugustThisYear = LocalDate.of(now.getYear(), Month.AUGUST, 31); @@ -294,10 +296,9 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us questionIsCorrect = questionIsCorrect && questionPartIsCorrect; } - // Convert QuestionPageDTO to ContentSummaryDTO and calculate completion state - ContentSummaryDTO contentSummaryDTO = contentSummarizerService.extractContentSummary(questionPageDTO); - contentSummaryDTO.setState(UserAttemptManager.getCompletionState(questionPartsTotal, questionPartsCorrect, questionPartsIncorrect)); - mostRecentlyAttemptedQuestionsQueue.add(contentSummaryDTO); + mostRecentlyAttemptedQuestionsQueue.add(questionPageDTO); + CompletionState completionState = UserAttemptManager.getCompletionState(questionPartsTotal, questionPartsCorrect, questionPartsIncorrect); + mostRecentlyAttemptedQuestionsState.add(completionState); // Tag Stats - Loop through the Question's tags: for (String tag : questionPageDTO.getTags()) { @@ -369,17 +370,38 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us correctQuestionsThisAcademicYear++; } } else { - incompleteQuestionPages.add(contentSummaryDTO); + if (incompleteQuestionPages.size() < PROGRESS_MAX_RECENT_QUESTIONS) { + ContentSummaryDTO dto = contentSummarizerService.extractContentSummary(questionPageDTO); + dto.setState(completionState); + if ((questionPageDTO.getDeprecated() == null || !questionPageDTO.getDeprecated())) { + incompleteQuestionPages.add(dto); + } else if (incompleteDeprecatedQuestionPages.size() + < (PROGRESS_MAX_RECENT_QUESTIONS - incompleteQuestionPages.size())) { + incompleteDeprecatedQuestionPages.add(dto); + } + } } } + // Iterate over parallel question and state queues to create ContentSummaryDTOs with state set + List mostRecentlyAttemptedQuestionsList = new ArrayList<>(); + Iterator questionIterator = mostRecentlyAttemptedQuestionsQueue.iterator(); + Iterator stateIterator = mostRecentlyAttemptedQuestionsState.iterator(); + while (questionIterator.hasNext() && stateIterator.hasNext()) { + ContentSummaryDTO dto = contentSummarizerService.extractContentSummary(questionIterator.next()); + dto.setState(stateIterator.next()); + mostRecentlyAttemptedQuestionsList.add(dto); + } + Collections.reverse(mostRecentlyAttemptedQuestionsList); // We want most-recent first order and streams cannot reverse. + + // List up to PROGRESS_MAX_RECENT_QUESTIONS non-deprecated question pages, or if that fails deprecated ones too + List questionsNotCompleteList = Stream.concat( + incompleteQuestionPages.stream(), + incompleteDeprecatedQuestionPages.stream().limit(PROGRESS_MAX_RECENT_QUESTIONS - incompleteQuestionPages.size()) + ).collect(Collectors.toList()); + // Collate all the information into the JSON response as a Map: Map questionInfo = Maps.newHashMap(); - List mostRecentlyAttemptedQuestionsList = new ArrayList<>(mostRecentlyAttemptedQuestionsQueue); - Collections.reverse(mostRecentlyAttemptedQuestionsList); // We want most-recent first order and streams cannot reverse. - List questionsNotCompleteList = incompleteQuestionPages.stream() - .sorted(Comparator.comparing(ContentSummaryDTO::getDeprecated, Comparator.nullsFirst(Comparator.naturalOrder()))) - .limit(PROGRESS_MAX_RECENT_QUESTIONS).collect(Collectors.toList()); questionInfo.put("totalQuestionsAttempted", attemptedQuestions); questionInfo.put("totalQuestionsCorrect", correctQuestions); From b5562d3c746aac56fb2078dac085ad8bdfae2231 Mon Sep 17 00:00:00 2001 From: Sol Dubock <94075844+sjd210@users.noreply.github.com> Date: Thu, 26 Feb 2026 14:24:23 +0000 Subject: [PATCH 6/6] Revert "Map to ContentSummaryDTO outside the loop" This reverts commit 6c5bf8a2e0116f1feb15e283ec1c48e897037c95. --- .../segue/api/managers/StatisticsManager.java | 48 +++++-------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java b/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java index 0e00f1c280..b241aa0564 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/segue/api/managers/StatisticsManager.java @@ -35,6 +35,7 @@ import uk.ac.cam.cl.dtg.isaac.dto.content.ContentDTO; import uk.ac.cam.cl.dtg.isaac.dto.content.ContentSummaryDTO; import uk.ac.cam.cl.dtg.isaac.dto.content.QuestionDTO; +import uk.ac.cam.cl.dtg.isaac.dto.content.SeguePageDTO; import uk.ac.cam.cl.dtg.isaac.dto.users.RegisteredUserDTO; import uk.ac.cam.cl.dtg.segue.dao.ILogManager; import uk.ac.cam.cl.dtg.segue.dao.SegueDatabaseException; @@ -48,14 +49,13 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Iterator; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Queue; import java.util.stream.Collectors; -import java.util.stream.Stream; import static com.google.common.collect.Maps.immutableEntry; import static uk.ac.cam.cl.dtg.isaac.api.Constants.*; @@ -198,9 +198,7 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us Map questionAttemptsByTypeStats = Maps.newHashMap(); Map questionsCorrectByTypeStats = Maps.newHashMap(); List incompleteQuestionPages = Lists.newArrayList(); - List incompleteDeprecatedQuestionPages = Lists.newArrayList(); - Queue mostRecentlyAttemptedQuestionsQueue = new CircularFifoQueue<>(PROGRESS_MAX_RECENT_QUESTIONS); - Queue mostRecentlyAttemptedQuestionsState = new CircularFifoQueue<>(PROGRESS_MAX_RECENT_QUESTIONS); + Queue mostRecentlyAttemptedQuestionsQueue = new CircularFifoQueue<>(PROGRESS_MAX_RECENT_QUESTIONS); LocalDate now = LocalDate.now(); LocalDate endOfAugustThisYear = LocalDate.of(now.getYear(), Month.AUGUST, 31); @@ -296,9 +294,10 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us questionIsCorrect = questionIsCorrect && questionPartIsCorrect; } - mostRecentlyAttemptedQuestionsQueue.add(questionPageDTO); - CompletionState completionState = UserAttemptManager.getCompletionState(questionPartsTotal, questionPartsCorrect, questionPartsIncorrect); - mostRecentlyAttemptedQuestionsState.add(completionState); + // Convert QuestionPageDTO to ContentSummaryDTO and calculate completion state + ContentSummaryDTO contentSummaryDTO = contentSummarizerService.extractContentSummary(questionPageDTO); + contentSummaryDTO.setState(UserAttemptManager.getCompletionState(questionPartsTotal, questionPartsCorrect, questionPartsIncorrect)); + mostRecentlyAttemptedQuestionsQueue.add(contentSummaryDTO); // Tag Stats - Loop through the Question's tags: for (String tag : questionPageDTO.getTags()) { @@ -370,38 +369,17 @@ public Map getUserQuestionInformation(final RegisteredUserDTO us correctQuestionsThisAcademicYear++; } } else { - if (incompleteQuestionPages.size() < PROGRESS_MAX_RECENT_QUESTIONS) { - ContentSummaryDTO dto = contentSummarizerService.extractContentSummary(questionPageDTO); - dto.setState(completionState); - if ((questionPageDTO.getDeprecated() == null || !questionPageDTO.getDeprecated())) { - incompleteQuestionPages.add(dto); - } else if (incompleteDeprecatedQuestionPages.size() - < (PROGRESS_MAX_RECENT_QUESTIONS - incompleteQuestionPages.size())) { - incompleteDeprecatedQuestionPages.add(dto); - } - } + incompleteQuestionPages.add(contentSummaryDTO); } } - // Iterate over parallel question and state queues to create ContentSummaryDTOs with state set - List mostRecentlyAttemptedQuestionsList = new ArrayList<>(); - Iterator questionIterator = mostRecentlyAttemptedQuestionsQueue.iterator(); - Iterator stateIterator = mostRecentlyAttemptedQuestionsState.iterator(); - while (questionIterator.hasNext() && stateIterator.hasNext()) { - ContentSummaryDTO dto = contentSummarizerService.extractContentSummary(questionIterator.next()); - dto.setState(stateIterator.next()); - mostRecentlyAttemptedQuestionsList.add(dto); - } - Collections.reverse(mostRecentlyAttemptedQuestionsList); // We want most-recent first order and streams cannot reverse. - - // List up to PROGRESS_MAX_RECENT_QUESTIONS non-deprecated question pages, or if that fails deprecated ones too - List questionsNotCompleteList = Stream.concat( - incompleteQuestionPages.stream(), - incompleteDeprecatedQuestionPages.stream().limit(PROGRESS_MAX_RECENT_QUESTIONS - incompleteQuestionPages.size()) - ).collect(Collectors.toList()); - // Collate all the information into the JSON response as a Map: Map questionInfo = Maps.newHashMap(); + List mostRecentlyAttemptedQuestionsList = new ArrayList<>(mostRecentlyAttemptedQuestionsQueue); + Collections.reverse(mostRecentlyAttemptedQuestionsList); // We want most-recent first order and streams cannot reverse. + List questionsNotCompleteList = incompleteQuestionPages.stream() + .sorted(Comparator.comparing(ContentSummaryDTO::getDeprecated, Comparator.nullsFirst(Comparator.naturalOrder()))) + .limit(PROGRESS_MAX_RECENT_QUESTIONS).collect(Collectors.toList()); questionInfo.put("totalQuestionsAttempted", attemptedQuestions); questionInfo.put("totalQuestionsCorrect", correctQuestions);