Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use lightweight validation responses for progress stats #669

Merged
merged 2 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package uk.ac.cam.cl.dtg.isaac.quiz;

import java.util.Date;
import java.util.List;
import java.util.Map;

import uk.ac.cam.cl.dtg.segue.api.Constants.TimeInterval;
import uk.ac.cam.cl.dtg.segue.dao.SegueDatabaseException;
import uk.ac.cam.cl.dtg.isaac.dos.LightweightQuestionValidationResponse;
import uk.ac.cam.cl.dtg.isaac.dos.QuestionValidationResponse;
import uk.ac.cam.cl.dtg.isaac.dos.users.Role;
import uk.ac.cam.cl.dtg.segue.api.Constants.*;
import uk.ac.cam.cl.dtg.segue.dao.SegueDatabaseException;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
* IQuestionAttemptManager. Objects implementing this interface are responsible for recording question attempts
Expand Down Expand Up @@ -46,6 +46,18 @@ void registerQuestionAttempt(final Long userId, final String questionPageId, fin
Map<String, Map<String, List<QuestionValidationResponse>>> getQuestionAttempts(final Long userId)
throws SegueDatabaseException;

/**
* Get a users question attempts in lightweight form, without full answer data.
*
* @param userId
* - the id of the user to search for.
* @return the questionAttempts map or an empty map if the user has not yet registered any attempts.
* @throws SegueDatabaseException
* - If there is a database error.
*/
Map<String, Map<String, List<LightweightQuestionValidationResponse>>> getLightweightQuestionAttempts(final Long userId)
throws SegueDatabaseException;

/**
* Get a users question attempts on a specific question page.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ public Map<Long, Map<String, Map<String, List<LightweightQuestionValidationRespo
+ " WHERE user_id = ANY(?) ORDER BY \"timestamp\" ASC";

Map<Long, Map<String, Map<String, List<LightweightQuestionValidationResponse>>>> mapToReturn
= userIds.stream().collect(Collectors.toMap(Function.identity(), k -> Maps.newHashMap()));
= userIds.stream().collect(Collectors.toMap(Function.identity(), k -> Maps.newLinkedHashMap()));

try (Connection conn = database.getDatabaseConnection();
PreparedStatement pst = conn.prepareStatement(query)) {
Expand All @@ -282,7 +282,12 @@ public Map<Long, Map<String, Map<String, List<LightweightQuestionValidationRespo
throw new SegueDatabaseException("Postgres exception", e);
}
}


@Override
public Map<String, Map<String, List<LightweightQuestionValidationResponse>>> getLightweightQuestionAttempts(Long userId) throws SegueDatabaseException {
return this.getLightweightQuestionAttemptsByUsers(Collections.singletonList(userId)).getOrDefault(userId, Collections.emptyMap());
}

@Override
public Map<Long, Map<String, Map<String, List<LightweightQuestionValidationResponse>>>>
getMatchingLightweightQuestionAttempts(final List<Long> userIds, final List<String> allQuestionPageIds)
Expand Down Expand Up @@ -461,10 +466,10 @@ private void augmentMapLightweightValidationResponseByUserPagePartWithResults(
Long userId = results.getLong("user_id");

Map<String, Map<String, List<LightweightQuestionValidationResponse>>> mapOfQuestionAttemptsByPage
= mapToAugment.computeIfAbsent(userId, k -> Maps.newHashMap());
= mapToAugment.computeIfAbsent(userId, k -> Maps.newLinkedHashMap());

Map<String, List<LightweightQuestionValidationResponse>> attemptsForThisQuestionPage
= mapOfQuestionAttemptsByPage.computeIfAbsent(questionPageId, k -> Maps.newHashMap());
= mapOfQuestionAttemptsByPage.computeIfAbsent(questionPageId, k -> Maps.newLinkedHashMap());

List<LightweightQuestionValidationResponse> listOfResponses
= attemptsForThisQuestionPage.computeIfAbsent(questionId, k -> Lists.newArrayList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,23 @@ public Map<String, Map<String, List<QuestionValidationResponse>>> getQuestionAtt
}
}

/**
* getLightweightQuestionAttemptsByUser. This method will return all of the question attempts for a given user as a map.
*
* Attempts will not be augmented with the full attempt JSON data.
*
* @param user
* - with the session information included.
* @return map of question attempts (QuestionPageId -> QuestionID -> [LightweightQuestionValidationResponse] or an empty map.
* @throws SegueDatabaseException
* - if there is a database error.
*/
public Map<String, Map<String, List<LightweightQuestionValidationResponse>>> getLightweightQuestionAttemptsByUser(final RegisteredUserDTO user)
throws SegueDatabaseException {
return this.questionAttemptPersistenceManager.getLightweightQuestionAttempts(user.getId());
}


/**
* Return all the attempts of a user at a specified page ID prefix.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import uk.ac.cam.cl.dtg.isaac.dos.AudienceContext;
import uk.ac.cam.cl.dtg.isaac.dos.Difficulty;
import uk.ac.cam.cl.dtg.isaac.dos.IUserStreaksManager;
import uk.ac.cam.cl.dtg.isaac.dos.QuestionValidationResponse;
import uk.ac.cam.cl.dtg.isaac.dos.LightweightQuestionValidationResponse;
import uk.ac.cam.cl.dtg.isaac.dos.Stage;
import uk.ac.cam.cl.dtg.isaac.dos.users.Role;
import uk.ac.cam.cl.dtg.isaac.dto.IsaacQuestionPageDTO;
Expand Down Expand Up @@ -206,15 +206,14 @@ public Map<String, Object> getUserQuestionInformation(final RegisteredUserDTO us
LocalDate lastDayOfPreviousAcademicYear =
now.isAfter(endOfAugustThisYear) ? endOfAugustThisYear : endOfAugustLastYear;

Map<String, Map<String, List<QuestionValidationResponse>>> questionAttemptsByUser = questionManager.getQuestionAttemptsByUser(userOfInterest);
Map<String, Map<String, List<LightweightQuestionValidationResponse>>> questionAttemptsByUser = questionManager.getLightweightQuestionAttemptsByUser(userOfInterest);
Map<String, ContentDTO> questionMap = this.getQuestionMap(questionAttemptsByUser.keySet());

// Loop through each Question attempted:
for (Entry<String, Map<String, List<QuestionValidationResponse>>> question : questionAttemptsByUser.entrySet()) {
for (Entry<String, Map<String, List<LightweightQuestionValidationResponse>>> question : questionAttemptsByUser.entrySet()) {
ContentDTO contentDTO = questionMap.get(question.getKey());
if (!(contentDTO instanceof IsaacQuestionPageDTO)) {
log.warn(String.format("Excluding unknown question (%s) from user progress statistics for user (%s)!",
question.getKey(), userOfInterest.getId()));
log.warn("Excluding unknown question ({}) from user progress statistics for user ({})!", question.getKey(), userOfInterest.getId());
// This content is missing, or it is not a question page; either way, exclude it.
continue;
}
Expand All @@ -236,7 +235,7 @@ public Map<String, Object> getUserQuestionInformation(final RegisteredUserDTO us
LocalDate mostRecentAttemptAtThisQuestionPart = null;

// Loop through each attempt at the Question Part if they have attempted it:
for (QuestionValidationResponse validationResponse : question.getValue().get(questionPart.getId())) {
for (LightweightQuestionValidationResponse validationResponse : question.getValue().get(questionPart.getId())) {
LocalDate dateAttempted = LocalDateTime.ofInstant(
validationResponse.getDateAttempted().toInstant(), ZoneId.systemDefault()).toLocalDate();
if (mostRecentAttemptAtThisQuestionPart == null || dateAttempted.isAfter(mostRecentAttemptAtThisQuestionPart)) {
Expand Down
Loading