use of de.tum.in.www1.artemis.service.scheduled.quiz.QuizScheduleService in project Artemis by ls1intum.
the class QuizSubmissionWebsocketService method saveSubmission.
// TODO it would be nice to have some kind of startQuiz call that creates the participation with an initialization date. This should happen when the quiz is first shown
// to the user. Then we also could find out how long students work on the quiz on average
/**
* Saves a quiz submission into the hash maps. Submitted quizzes are marked to be saved into the database in the QuizScheduleService
*
* @param exerciseId the exerciseID to the corresponding QuizExercise
* @param quizSubmission the submission which should be saved
* @param principal refers to the user who initiated the request
*/
@MessageMapping("/topic/quizExercise/{exerciseId}/submission")
public void saveSubmission(@DestinationVariable Long exerciseId, @Payload QuizSubmission quizSubmission, Principal principal) {
// Without this, custom jpa repository methods don't work in websocket channel.
SecurityUtils.setAuthorizationObject();
String username = principal.getName();
try {
QuizSubmission updatedQuizSubmission = quizSubmissionService.saveSubmissionForLiveMode(exerciseId, quizSubmission, username, false);
// send updated submission over websocket (use a thread to prevent that the outbound channel blocks the inbound channel (e.g. due a slow client))
// to improve the performance, this is currently deactivated: slow clients might lead to bottlenecks so that more important messages can not be distributed any more
// new Thread(() -> sendSubmissionToUser(username, exerciseId, quizSubmission)).start();
// log.info("WS.Inbound: Sent quiz submission (async) back to user {} in quiz {} after {} µs ", principal.getName(), exerciseId, (System.nanoTime() - start) / 1000);
} catch (QuizSubmissionException ex) {
// send error message over websocket (use a thread to prevent that the outbound channel blocks the inbound channel (e.g. due a slow client))
new Thread(() -> messagingTemplate.convertAndSendToUser(username, "/topic/quizExercise/" + exerciseId + "/submission", new WebsocketError(ex.getMessage()))).start();
}
}
use of de.tum.in.www1.artemis.service.scheduled.quiz.QuizScheduleService in project ArTEMiS by ls1intum.
the class QuizSubmissionWebsocketService method saveSubmission.
// TODO it would be nice to have some kind of startQuiz call that creates the participation with an initialization date. This should happen when the quiz is first shown
// to the user. Then we also could find out how long students work on the quiz on average
/**
* Saves a quiz submission into the hash maps. Submitted quizzes are marked to be saved into the database in the QuizScheduleService
*
* @param exerciseId the exerciseID to the corresponding QuizExercise
* @param quizSubmission the submission which should be saved
* @param principal refers to the user who initiated the request
*/
@MessageMapping("/topic/quizExercise/{exerciseId}/submission")
public void saveSubmission(@DestinationVariable Long exerciseId, @Payload QuizSubmission quizSubmission, Principal principal) {
// Without this, custom jpa repository methods don't work in websocket channel.
SecurityUtils.setAuthorizationObject();
String username = principal.getName();
try {
QuizSubmission updatedQuizSubmission = quizSubmissionService.saveSubmissionForLiveMode(exerciseId, quizSubmission, username, false);
// send updated submission over websocket (use a thread to prevent that the outbound channel blocks the inbound channel (e.g. due a slow client))
// to improve the performance, this is currently deactivated: slow clients might lead to bottlenecks so that more important messages can not be distributed any more
// new Thread(() -> sendSubmissionToUser(username, exerciseId, quizSubmission)).start();
// log.info("WS.Inbound: Sent quiz submission (async) back to user {} in quiz {} after {} µs ", principal.getName(), exerciseId, (System.nanoTime() - start) / 1000);
} catch (QuizSubmissionException ex) {
// send error message over websocket (use a thread to prevent that the outbound channel blocks the inbound channel (e.g. due a slow client))
new Thread(() -> messagingTemplate.convertAndSendToUser(username, "/topic/quizExercise/" + exerciseId + "/submission", new WebsocketError(ex.getMessage()))).start();
}
}
use of de.tum.in.www1.artemis.service.scheduled.quiz.QuizScheduleService in project ArTEMiS by ls1intum.
the class QuizSubmissionService method checkSubmissionForLiveModeOrThrow.
/**
* Check that the user is allowed to currently submit to the specified exercise and throws an exception if not
*/
private void checkSubmissionForLiveModeOrThrow(Long exerciseId, String userLogin, String logText, long start) throws QuizSubmissionException {
// check if submission is still allowed
QuizExercise quizExercise = quizScheduleService.getQuizExercise(exerciseId);
if (quizExercise == null) {
// Fallback solution
log.info("Quiz not in QuizScheduleService cache, fetching from DB");
quizExercise = quizExerciseRepository.findByIdElseThrow(exerciseId);
quizExercise.setQuizBatches(null);
}
log.debug("{}: Received quiz exercise for user {} in quiz {} in {} µs.", logText, userLogin, exerciseId, (System.nanoTime() - start) / 1000);
if (!quizExercise.isQuizStarted() || quizExercise.isQuizEnded()) {
throw new QuizSubmissionException("The quiz is not active");
}
var cachedSubmission = quizScheduleService.getQuizSubmission(exerciseId, userLogin);
if (cachedSubmission.isSubmitted()) {
// the old submission has not yet been processed, so don't allow a new one yet
throw new QuizSubmissionException("You have already submitted the quiz");
}
if (quizExercise.getQuizMode() == QuizMode.SYNCHRONIZED) {
// the batch exists if the quiz is active, otherwise a new inactive batch is returned
if (!quizBatchService.getOrCreateSynchronizedQuizBatch(quizExercise).isSubmissionAllowed()) {
throw new QuizSubmissionException("The quiz is not active");
}
// in synchronized mode we cache the participation after we processed the submission, so we can check there if the submission was already processed
var cachedParticipation = quizScheduleService.getParticipation(exerciseId, userLogin);
if (cachedParticipation != null && cachedParticipation.getResults().stream().anyMatch(r -> r.getSubmission().isSubmitted())) {
throw new QuizSubmissionException("You have already submitted the quiz");
}
} else {
// in the other modes the resubmission checks are done at join time and the student-batch association is removed when processing a submission
var batch = quizBatchService.getQuizBatchForStudentByLogin(quizExercise, userLogin);
// there is no way of distinguishing these two error cases without an extra db query
if (batch.isEmpty()) {
throw new QuizSubmissionException("You did not join or have already submitted the quiz");
}
if (!batch.get().isSubmissionAllowed()) {
throw new QuizSubmissionException("The quiz is not active");
}
}
// TODO: additional checks that may be beneficial
// for example it is possible for students that are not members of the course to submit the quiz
// but for performance reasons the checks may have to be done in the quiz submission service where no feedback for the students can be generated
}
use of de.tum.in.www1.artemis.service.scheduled.quiz.QuizScheduleService in project ArTEMiS by ls1intum.
the class QuizSubmissionService method saveSubmissionForLiveMode.
/**
* Saves a quiz submission into the hash maps for live quizzes. Submitted quizzes are marked to be saved into the database in the QuizScheduleService
*
* @param exerciseId the exerciseID to the corresponding QuizExercise
* @param quizSubmission the submission which should be saved
* @param userLogin the login of the user who has initiated the request
* @param submitted whether the user has pressed the submit button or not
*
* @return the updated quiz submission object
* @throws QuizSubmissionException handles errors, e.g. when the live quiz has already ended, or when the quiz was already submitted before
*/
public QuizSubmission saveSubmissionForLiveMode(Long exerciseId, QuizSubmission quizSubmission, String userLogin, boolean submitted) throws QuizSubmissionException {
// TODO: what happens if a user executes this call twice in the same moment (using 2 threads)
String logText = submitted ? "submit quiz in live mode:" : "save quiz in live mode:";
long start = System.nanoTime();
checkSubmissionForLiveModeOrThrow(exerciseId, userLogin, logText, start);
// recreate pointers back to submission in each submitted answer
for (SubmittedAnswer submittedAnswer : quizSubmission.getSubmittedAnswers()) {
submittedAnswer.setSubmission(quizSubmission);
}
// set submission date
quizSubmission.setSubmissionDate(ZonedDateTime.now());
// save submission to HashMap
quizScheduleService.updateSubmission(exerciseId, userLogin, quizSubmission);
log.info("{} Saved quiz submission for user {} in quiz {} after {} µs ", logText, userLogin, exerciseId, (System.nanoTime() - start) / 1000);
return quizSubmission;
}
use of de.tum.in.www1.artemis.service.scheduled.quiz.QuizScheduleService in project Artemis by ls1intum.
the class QuizSubmissionService method saveSubmissionForLiveMode.
/**
* Saves a quiz submission into the hash maps for live quizzes. Submitted quizzes are marked to be saved into the database in the QuizScheduleService
*
* @param exerciseId the exerciseID to the corresponding QuizExercise
* @param quizSubmission the submission which should be saved
* @param userLogin the login of the user who has initiated the request
* @param submitted whether the user has pressed the submit button or not
*
* @return the updated quiz submission object
* @throws QuizSubmissionException handles errors, e.g. when the live quiz has already ended, or when the quiz was already submitted before
*/
public QuizSubmission saveSubmissionForLiveMode(Long exerciseId, QuizSubmission quizSubmission, String userLogin, boolean submitted) throws QuizSubmissionException {
// TODO: what happens if a user executes this call twice in the same moment (using 2 threads)
String logText = submitted ? "submit quiz in live mode:" : "save quiz in live mode:";
long start = System.nanoTime();
checkSubmissionForLiveModeOrThrow(exerciseId, userLogin, logText, start);
// recreate pointers back to submission in each submitted answer
for (SubmittedAnswer submittedAnswer : quizSubmission.getSubmittedAnswers()) {
submittedAnswer.setSubmission(quizSubmission);
}
// set submission date
quizSubmission.setSubmissionDate(ZonedDateTime.now());
// save submission to HashMap
quizScheduleService.updateSubmission(exerciseId, userLogin, quizSubmission);
log.info("{} Saved quiz submission for user {} in quiz {} after {} µs ", logText, userLogin, exerciseId, (System.nanoTime() - start) / 1000);
return quizSubmission;
}
Aggregations